1 module FuncTorrent.FileSystem
11 import Control.Concurrent (ThreadId, forkIO)
12 import Control.Concurrent.Chan (Chan, newChan, readChan, writeChan)
13 import Control.Concurrent.MVar (MVar, putMVar)
14 import Control.Monad (forever)
15 import Data.Map (traverseWithKey)
16 import qualified Data.ByteString as BS
18 import System.IO (Handle, IOMode (ReadWriteMode), withFile)
19 import System.Directory (doesFileExist)
21 import FuncTorrent.PieceManager (PieceDlState(..), PieceData(..), PieceMap, pieceNumToOffset)
22 import FuncTorrent.Utils (createDummyFile, readFileAtOffset, writeFileAtOffset, verifyHash)
24 type PieceNum = Integer
25 data Piece = Piece PieceNum BS.ByteString
27 data Msg = ReadPiece PieceNum Integer (MVar Piece)
29 | VerifyPiece PieceNum (MVar Bool)
31 type MsgChannel = Chan Msg
33 createMsgChannel :: IO (Chan Msg)
34 createMsgChannel = newChan
36 startThread :: PieceMap -> MsgChannel -> Handle -> IO ThreadId
37 startThread pieceMap c handle = do
38 forkIO $ forever $ recvMsg >>= sendResponse
43 ReadPiece n len' var -> do
44 bs <- readPiece n len'
45 putMVar var (Piece n bs)
46 WritePiece (Piece n bs) -> do
48 VerifyPiece n var -> do
49 isHashValid <- verifyPiece n
50 putMVar var isHashValid
52 let offset = pieceNumToOffset pieceMap n
53 readFileAtOffset handle offset len'
54 writePiece n piece = do
55 let offset = pieceNumToOffset pieceMap n
56 writeFileAtOffset handle offset piece
58 let offset = pieceNumToOffset pieceMap n
59 hash' = hash (pieceMap ! n)
60 len' = len (pieceMap ! n)
61 bs' <- readFileAtOffset handle offset len'
62 return $ verifyHash bs' hash'
64 pieceMapFromFile :: FilePath -> Integer -> PieceMap -> IO PieceMap
65 pieceMapFromFile filePath fileLen pieceMap = do
66 dfe <- doesFileExist filePath
68 then traverseWithKey f pieceMap
69 else createDummyFile filePath (fromIntegral fileLen) >> return pieceMap
72 let offset = pieceNumToOffset pieceMap k
73 isHashValid <- flip verifyHash (hash v) <$> withFile filePath ReadWriteMode (\handle -> readFileAtOffset handle offset (len v))
75 then return $ v { dlstate = Have }
78 writePiece :: MsgChannel -> PieceNum -> BS.ByteString -> IO ()
79 writePiece c pieceNum bs = do
80 writeChan c $ WritePiece (Piece pieceNum bs)