]> git.rkrishnan.org Git - functorrent.git/commitdiff
Resume download from where it was left off last time
authorRamakrishnan Muthukrishnan <ram@rkrishnan.org>
Tue, 15 Sep 2015 16:11:36 +0000 (21:41 +0530)
committerRamakrishnan Muthukrishnan <ram@rkrishnan.org>
Tue, 15 Sep 2015 16:25:15 +0000 (21:55 +0530)
This commit looks at the downloaded file, verifies the hashes of
the downloaded pieces, marks them as "have" pieces and proceeds
to download those that are not in its posession.

src/FuncTorrent/Fileops.hs
src/FuncTorrent/Peer.hs
src/main/Main.hs

index f1bbefe1126df9e78693726caa9c0481fe428f9d..cab73c7be6f47a417acdf824ea0f0fa084060f13 100644 (file)
@@ -1,17 +1,23 @@
 module FuncTorrent.Fileops
        (createDummyFile,
-        writeFileAtOffset
+        writeFileAtOffset,
+        readFileAtOffset
        ) where
 
 import Prelude hiding (writeFile)
 
 import System.IO (withFile, hSeek, IOMode(..), SeekMode(..))
-import Data.ByteString (ByteString, writeFile, hPut)
+import System.Directory (doesFileExist)
+import Data.ByteString (ByteString, writeFile, hPut, hGet)
 import qualified Data.ByteString.Char8 as BC (replicate)
 
 createDummyFile :: FilePath -> Int -> IO ()
-createDummyFile path size =
-  writeFile path (BC.replicate size '\0')
+createDummyFile path size = do
+  dfe <- doesFileExist path
+  if dfe
+    then return ()
+    else
+    writeFile path (BC.replicate size '\0')
 
 -- write into a file at a specific offet
 writeFileAtOffset :: FilePath -> Integer -> ByteString -> IO ()
@@ -19,3 +25,8 @@ writeFileAtOffset path offset block =
   withFile path ReadWriteMode (\h -> do
                                   hSeek h AbsoluteSeek offset
                                   hPut h block)
+readFileAtOffset :: FilePath -> Integer -> Integer -> IO ByteString
+readFileAtOffset path offset len =
+  withFile path ReadWriteMode (\h -> do
+                                  hSeek h AbsoluteSeek offset
+                                  hGet h (fromInteger len))
index 61b5112c7811d43e765a4d59e01bb8d68e546999..2b9ff5ffd2264d26c311407d697ed4a0f7e6418e 100644 (file)
@@ -8,19 +8,20 @@ module FuncTorrent.Peer
 import Prelude hiding (lookup, concat, replicate, splitAt, take, filter)
 
 import System.IO (Handle, BufferMode(..), hSetBuffering)
+import System.Directory (doesFileExist)
 import Data.ByteString (ByteString, unpack, concat, hGet, hPut, take, empty)
 import qualified Data.ByteString.Char8 as BC (length)
 import Network (connectTo, PortID(..))
 import Control.Monad.State
 import Data.Bits
 import Data.Word (Word8)
-import Data.Map (Map, fromList, toList, (!), mapWithKey, adjust, filter)
+import Data.Map (Map, fromList, toList, (!), mapWithKey, traverseWithKey, adjust, filter)
 import qualified Crypto.Hash.SHA1 as SHA1 (hash)
 import Safe (headMay)
 
 import FuncTorrent.Metainfo (Info(..), Metainfo(..))
 import FuncTorrent.Utils (splitN, splitNum)
-import FuncTorrent.Fileops (createDummyFile, writeFileAtOffset)
+import FuncTorrent.Fileops (createDummyFile, writeFileAtOffset, readFileAtOffset)
 import FuncTorrent.PeerMsgs (Peer(..), PeerMsg(..), sendMsg, getMsg, genHandshakeMsg)
 
 data PState = PState { handle :: Handle
@@ -61,6 +62,25 @@ initPieceMap pieceHash fileLen pieceLen = fromList kvs
     hashes = splitN 20 pieceHash
     pLengths = (splitNum fileLen pieceLen)
 
+updatePieceMap :: FilePath -> PieceMap -> IO PieceMap
+updatePieceMap filePath pieceMap = do
+  dfe <- doesFileExist filePath
+  -- TODO: this is not enough, file should have the same size as well
+  if dfe
+    then pieceMapFromFile filePath pieceMap
+    else return pieceMap
+
+pieceMapFromFile :: FilePath -> PieceMap -> IO PieceMap
+pieceMapFromFile filePath pieceMap = do
+  traverseWithKey f pieceMap
+    where
+      f k v = do
+        let offset = if k == 0 then 0 else k * len (pieceMap ! (k - 1))
+        isHashValid <- (flip verifyHash) (hash v) <$> (readFileAtOffset filePath offset (len v))
+        if isHashValid
+          then return $ v { dlstate = Have }
+          else return $ v
+
 havePiece :: PieceMap -> Integer -> Bool
 havePiece pm index =
   dlstate (pm ! index) == Have
@@ -130,8 +150,9 @@ handlePeerMsgs p peerId m = do
       fileLen = lengthInBytes (info m)
       fileName = name (info m)
       pieceStatus = initPieceMap pieceHash fileLen pLen
+  pieceStatus' <- updatePieceMap fileName pieceStatus
   createDummyFile fileName (fromIntegral fileLen)
-  _ <- runStateT (msgLoop pieceStatus fileName) pstate
+  _ <- runStateT (msgLoop pieceStatus' fileName) pstate
   return ()
 
 msgLoop :: PieceMap -> FilePath -> StateT PState IO ()
index 0a514104703485beda12b9206226fe44acbb6bc5..ae3c0354e76a7b714db629388211551bd091600f 100644 (file)
@@ -57,9 +57,13 @@ main = do
     case torrentToMetainfo torrentStr of
      Left e -> logError e log
      Right m -> do
-       log "Input File OK"
-       log $ "Downloading file : " ++ name (info m)
-
+       let p = name (info m)
+       log $ "Downloading file : " ++ p
+       -- if we had downloaded the file before (partly or completely)
+       -- then we should check the current directory for the existence
+       -- of the file and then update the map of each piece' availability.
+       -- This can be donw by reading each piece and verifying the checksum.
+       -- If the checksum does not match, we don't have that piece.
        log $ "starting server"
        (serverSock, (PortNumber portnum)) <- Server.start
        log $ "server started on " ++ show portnum