+++ /dev/null
-{-
- - Copyright (C) 2015-2016 Ramakrishnan Muthukrishnan <ram@rkrishnan.org>
- -
- - This file is part of FuncTorrent.
- -
- - FuncTorrent is free software; you can redistribute it and/or modify
- - it under the terms of the GNU General Public License as published by
- - the Free Software Foundation; either version 3 of the License, or
- - (at your option) any later version.
- -
- - FuncTorrent is distributed in the hope that it will be useful,
- - but WITHOUT ANY WARRANTY; without even the implied warranty of
- - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- - GNU General Public License for more details.
- -
- - You should have received a copy of the GNU General Public License
- - along with FuncTorrent; if not, see <http://www.gnu.org/licenses/>
- -}
-
-{-# LANGUAGE OverloadedStrings #-}
-{-# LANGUAGE FlexibleContexts #-}
-
-module FuncTorrent.FileSystem
- (run,
- MsgChannel,
- createMsgChannel,
- writePieceToDisk,
- Piece(..),
- pieceMapFromFile,
- Stats(..),
- getStats
- )
- where
-
-import Control.Concurrent.Chan (Chan, newChan, readChan, writeChan)
-import Control.Concurrent.MVar (MVar, newEmptyMVar, putMVar)
-import Control.Monad (forever)
-import Control.Monad.State (StateT, liftIO, get, runStateT, modify)
-import qualified Data.ByteString as BS
-import Data.Map (traverseWithKey, (!))
-import System.IO (Handle, IOMode (ReadWriteMode), withFile)
-import System.Directory (doesFileExist)
-
-import FuncTorrent.PieceManager (PieceDlState(..), PieceData(..), PieceMap, pieceNumToOffset)
-import FuncTorrent.Utils (createDummyFile, readFileAtOffset, writeFileAtOffset, verifyHash)
-
-type PieceNum = Integer
-data Piece = Piece PieceNum BS.ByteString
-
-data Msg = ReadPiece PieceNum Integer (MVar Piece)
- | WritePiece Piece
- | VerifyPiece PieceNum (MVar Bool)
- | GetStats (MVar Stats)
-
-type MsgChannel = Chan Msg
-
-data Stats = Stats { bytesRead :: Integer
- , bytesWritten :: Integer
- }
-
-createMsgChannel :: IO (Chan Msg)
-createMsgChannel = newChan
-
-run :: PieceMap -> MsgChannel -> Handle -> IO ()
-run pieceMap c handle = forever $ do
- _ <- runStateT (run' pieceMap c handle) initialStats
- return ()
- where initialStats = Stats { bytesRead = 0
- , bytesWritten = 0 }
-
-run' :: PieceMap -> MsgChannel -> Handle -> StateT Stats IO ()
-run' pieceMap c handle = do
- stats <- get
- msg <- liftIO recvMsg
- liftIO $ sendResponse msg stats
- updateStats msg
- where
- recvMsg = readChan c
- sendResponse msg stats =
- case msg of
- ReadPiece n len' var -> do
- bs <- readPiece n len'
- putMVar var (Piece n bs)
- WritePiece (Piece n bs) ->
- writePiece n bs
- VerifyPiece n var -> do
- isHashValid <- verifyPiece n
- putMVar var isHashValid
- GetStats var ->
- putMVar var stats
- readPiece n len' = do
- let offset = pieceNumToOffset pieceMap n
- readFileAtOffset handle offset len'
- writePiece n piece = do
- let offset = pieceNumToOffset pieceMap n
- writeFileAtOffset handle offset piece
- verifyPiece n = do
- let offset = pieceNumToOffset pieceMap n
- hash' = hash (pieceMap ! n)
- len' = len (pieceMap ! n)
- bs' <- readFileAtOffset handle offset len'
- return $ verifyHash bs' hash'
- updateStats (ReadPiece _ l _) =
- modify (\st -> st {bytesRead = bytesRead st + l})
- updateStats (WritePiece (Piece _ bs)) =
- modify (\st -> st {bytesWritten = bytesWritten st + fromIntegral (BS.length bs)})
- updateStats _ = modify id
-
-pieceMapFromFile :: FilePath -> Integer -> PieceMap -> IO PieceMap
-pieceMapFromFile filePath fileLen pieceMap = do
- dfe <- doesFileExist filePath
- if dfe
- then traverseWithKey f pieceMap
- else createDummyFile filePath (fromIntegral fileLen) >> return pieceMap
- where
- f k v = do
- let offset = pieceNumToOffset pieceMap k
- isHashValid <- flip verifyHash (hash v) <$> withFile filePath ReadWriteMode (\handle -> readFileAtOffset handle offset (len v))
- if isHashValid
- then return $ v { dlstate = Have }
- else return v
-
-writePieceToDisk :: MsgChannel -> PieceNum -> BS.ByteString -> IO ()
-writePieceToDisk c pieceNum bs =
- writeChan c $ WritePiece (Piece pieceNum bs)
-
-getStats :: MsgChannel -> IO (MVar Stats)
-getStats c = do
- v <- newEmptyMVar
- writeChan c $ GetStats v
- return v