packages feed

vhd-0.1: Vhd.hs

import Data.VHD
import Data.VHD.Types
import Data.VHD.Block
import Data.VHD.Context
import Data.VHD.Bat
import Data.VHD.CheckSum
import System.Environment (getArgs)
import System.IO
import Text.Printf
import Control.Monad
import Data.Char

import Data.IORef

import qualified Data.ByteString as B

showBlockSize i
	| i < 1024     = printf "%d bytes" i
	| i < (1024^2) = printf "%d kilobytes" (i`div`1024)
	| i < (1024^3) = printf "%d megabytes" (i`div`(1024^2))
	| otherwise    = printf "%d gigabytes" (i`div`(1024^3))

cmdCreate [name,size] = create name $ defaultCreateParameters { size = (read size) * 1024 * 1024, useBatmap = True }
cmdCreate _           = error "usage: create <name> <size Mb>"

cmdRead [file] = withVhdContext file $ \ctx -> do
	let hdr = ctxHeader ctx
	let ftr = ctxFooter ctx
	mapM_ (\(f,s) -> putStrLn (f ++ " : " ++ s))
		[ ("cookie           ", show $ headerCookie hdr)
		, ("version          ", show $ headerVersion hdr)
		, ("max-table-entries", show $ headerMaxTableEntries hdr)
		, ("block-size       ", showBlockSize $ headerBlockSize hdr)
		, ("header-checksum  ", printf "%08x (%s)" (headerCheckSum hdr)
		                                           (if verifyHeaderChecksum hdr then "valid" else "invalid"))
		, ("parent-uuid      ", show $ headerParentUniqueId hdr)
		, ("parent-filepath  ", show $ headerParentUnicodeName hdr)
		, ("parent-timestamp ", show $ headerParentTimeStamp hdr)
		]
	mapM_ (\(f,s) -> putStrLn (f ++ " : " ++ s))
		[ ("disk-geometry    ", show $ footerDiskGeometry ftr)
		, ("original-size    ", showBlockSize $ footerOriginalSize ftr)
		, ("current-size     ", showBlockSize $ footerOriginalSize ftr)
		, ("type             ", show $ footerDiskType ftr)
		, ("footer-checksum  ", printf "%08x (%s)" (footerCheckSum ftr)
		                                           (if verifyFooterChecksum ftr then "valid" else "invalid"))
		, ("uuid             ", show $ footerUniqueId ftr)
		, ("timestamp        ", show $ footerTimeStamp ftr)
		]
	allocated <- newIORef 0
	batIterate (ctxBatPtr ctx) (fromIntegral $ headerMaxTableEntries hdr) $ \i n -> do
		unless (n == 0xffffffff) $ modifyIORef allocated ((+) 1) >> printf "BAT[%.5x] = %08x\n" i n
	nb <- readIORef allocated
	putStrLn ("block allocated   : " ++ show nb ++ "/" ++ show (headerMaxTableEntries hdr))
cmdRead _ = error "usage: read <file>"

cmdPropGet [file, key] = withVhdContext file $ \ctx -> do
	case map toLower key of
		"max-table-entries"   -> putStrLn $ show $ headerMaxTableEntries $ ctxHeader ctx
		"blocksize"           -> putStrLn $ show $ headerBlockSize $ ctxHeader ctx
		"disk-type"           -> putStrLn $ show $ footerDiskType $ ctxFooter ctx
		"current-size"        -> putStrLn $ show $ footerCurrentSize $ ctxFooter ctx
		"uuid"                -> putStrLn $ show $ footerUniqueId $ ctxFooter ctx
		"parent-uuid"         -> putStrLn $ show $ headerParentUniqueId $ ctxHeader ctx
		"parent-timestamp"    -> putStrLn $ show $ headerParentTimeStamp $ ctxHeader ctx
		"parent-filepath"     -> putStrLn $ show $ headerParentUnicodeName $ ctxHeader ctx
		"timestamp"           -> putStrLn $ show $ footerTimeStamp $ ctxFooter ctx
		_                     -> error "unknown key"
cmdPropGet _ = error "usage: prop-get <file> <key>"

cmdConvert [fileRaw,fileVhd,size] = do
	create fileVhd $ defaultCreateParameters
		{ size = read size * 1024 * 1024
		, useBatmap = True
		}
	withVhdContext fileVhd $ \ctx -> do
		withFile fileRaw ReadMode $ \handle -> do
			loop ctx handle 0
	where
		loop ctx handle offset = do
			srcblock <- B.hGet handle (fromIntegral blockSize)
			if B.null srcblock
				then return ()
				else do
					unless (isBlockZero srcblock) $ do
						let blockNb = offset `div` fromIntegral blockSize
						extendBlock ctx blockNb
						sectorOff <- batRead (ctxBatPtr ctx) blockNb
						withBlock (ctxFilePath ctx) (headerBlockSize $ ctxHeader ctx) sectorOff $ \block ->
							writeBlock block srcblock 0
						putStrLn ("block " ++ show (offset `div` fromIntegral blockSize) ++ " written")
					-- do something with block
					loop ctx handle (offset + fromIntegral (B.length srcblock))


		isBlockZero = B.all ((==) 0)
		blockSize = 2 * 1024 * 1024
cmdConvert _ = error "usage: convert <raw file> <vhd file> <size Mb>"

main = do
	args <- getArgs
	case args of
		"create":xs   -> cmdCreate xs
		"read":xs     -> cmdRead xs
		"convert":xs  -> cmdConvert xs
		"prop-get":xs -> cmdPropGet xs