Replaced .NET hashing with OpenSSL
[pithos-ms-client] / trunk / Pithos.Core / Agents / BlockUpdater.cs
index ac70743..b39ca18 100644 (file)
-#region
-/* -----------------------------------------------------------------------
- * <copyright file="BlockUpdater.cs" company="GRNet">
- * 
- * Copyright 2011-2012 GRNET S.A. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or
- * without modification, are permitted provided that the following
- * conditions are met:
- *
- *   1. Redistributions of source code must retain the above
- *      copyright notice, this list of conditions and the following
- *      disclaimer.
- *
- *   2. Redistributions in binary form must reproduce the above
- *      copyright notice, this list of conditions and the following
- *      disclaimer in the documentation and/or other materials
- *      provided with the distribution.
- *
- *
- * THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
- * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
- * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- *
- * The views and conclusions contained in the software and
- * documentation are those of the authors and should not be
- * interpreted as representing official policies, either expressed
- * or implied, of GRNET S.A.
- * </copyright>
- * -----------------------------------------------------------------------
- */
-#endregion
-using System;
-using System.Collections.Concurrent;
-using System.Collections.Generic;
-using System.Diagnostics.Contracts;
-using System.IO;
-using System.Linq;
-using System.Reflection;
-using System.Security.Cryptography;
-using System.Text;
-using System.Threading.Tasks;
-using Pithos.Network;
-
-namespace Pithos.Core.Agents
-{
-    class BlockUpdater
-    {
-        //TODO: Must clean orphaned blocks from the Cache folder.
-        //
-        //The Cache folder may have orphaned blocks. Blocks may be left in the Cache folder because:
-        //1. A download was in progress when the application terminated. These blocks are needed to proceed 
-        //  with partial download
-        //2. The application terminated abnormally before the blocks were cleared after a download
-        //3. The server file was deleted before the download completed.
-        //
-        //In #1, we need to keep the blocks. We need to detect the other cases and delete orphans
-        //
-        //Mitigations:
-        // - Delete blocks with no corresponding state
-        // - Check and delete possible orphans when a Deletion is detected
-        // - Add Advanced command "Clear Cache"
-        //
-        //Need a better way to differentiate between cases #2, #3 and #1
-
-        private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
-
-        public string FilePath { get; private set; }
-        public string RelativePath { get; private set; }
-
-        public string CachePath { get; private set; }
-
-        public TreeHash ServerHash { get; private set; }
-
-        public string TempPath { get; private set; }
-
-        public bool HasBlocks
-        {
-            get { return _blocks.Count>0; }            
-        }
-
-        readonly ConcurrentDictionary<long, string> _blocks = new ConcurrentDictionary<long, string>();
-        readonly ConcurrentDictionary<string, string> _orphanBlocks = new ConcurrentDictionary<string, string>();
-
-        [ContractInvariantMethod]
-        private void Invariants()
-        {
-            Contract.Invariant(Path.IsPathRooted(CachePath));
-            Contract.Invariant(Path.IsPathRooted(FilePath));
-            Contract.Invariant(Path.IsPathRooted(TempPath));
-            Contract.Invariant(!Path.IsPathRooted(RelativePath));
-            Contract.Invariant(_blocks!=null);
-            Contract.Invariant(_orphanBlocks!=null);
-            Contract.Invariant(ServerHash!=null);
-        }
-
-        public BlockUpdater(string cachePath, string filePath, string relativePath,TreeHash serverHash)
-        {   
-            if (String.IsNullOrWhiteSpace(cachePath))
-                throw new ArgumentNullException("cachePath");
-            if (!Path.IsPathRooted(cachePath))
-                throw new ArgumentException("The cachePath must be rooted", "cachePath");
-            
-            if (string.IsNullOrWhiteSpace(filePath))
-                throw new ArgumentNullException("filePath");
-            if (!Path.IsPathRooted(filePath))
-                throw new ArgumentException("The filePath must be rooted", "filePath");
-            
-            if (string.IsNullOrWhiteSpace(relativePath))
-                throw new ArgumentNullException("relativePath");
-            if (Path.IsPathRooted(relativePath))
-                throw new ArgumentException("The relativePath must NOT be rooted", "relativePath");
-
-            if (serverHash == null)
-                throw new ArgumentNullException("serverHash");
-            Contract.EndContractBlock();
-
-            CachePath=cachePath;
-            FilePath = filePath;
-            RelativePath=relativePath;
-            ServerHash = serverHash;
-            //The file will be stored in a temporary location while downloading with an extension .download
-            TempPath = Path.Combine(CachePath, RelativePath + ".download");
-            
-            //Need to calculate the directory path because RelativePath may include folders
-            var directoryPath = Path.GetDirectoryName(TempPath);            
-            //directoryPath CAN be null if TempPath is a root path
-            if (String.IsNullOrWhiteSpace(directoryPath))
-                throw new ArgumentException("TempPath");
-            //CachePath was absolute so directoryPath is absolute too
-            Contract.Assume(Path.IsPathRooted(directoryPath));
-            
-            if (!Directory.Exists(directoryPath))
-                Directory.CreateDirectory(directoryPath);
-
-            LoadOrphans(directoryPath);
-        }
-
-        private void LoadOrphans(string directoryPath)
-        {
-            if (string.IsNullOrWhiteSpace(directoryPath))
-                throw new ArgumentNullException("directoryPath");
-            if (!Path.IsPathRooted(directoryPath))
-                throw new ArgumentException("The directoryPath must be rooted", "directoryPath");
-            if (ServerHash==null)
-                throw new InvalidOperationException("ServerHash wasn't initialized");
-            Contract.EndContractBlock();
-
-            var fileNamename = Path.GetFileName(FilePath);
-            var orphans = Directory.GetFiles(directoryPath, fileNamename + ".*");
-            foreach (var orphan in orphans)
-            {
-                using (HashAlgorithm hasher = HashAlgorithm.Create(ServerHash.BlockHash))
-                {
-                    var buffer=File.ReadAllBytes(orphan);
-                    //The server truncates nulls before calculating hashes, have to do the same
-                    //Find the last non-null byte, starting from the end
-                    var lastByteIndex = Array.FindLastIndex(buffer, buffer.Length-1, aByte => aByte != 0);
-                    //lastByteIndex may be -1 if the file was empty. We don't want to use that block file
-                    if (lastByteIndex >= 0)
-                    {
-                        var binHash = hasher.ComputeHash(buffer, 0, lastByteIndex);
-                        var hash = binHash.ToHashString();
-                        _orphanBlocks[hash] = orphan;
-                    }
-                }
-            }
-        }
-
-
-        public void Commit()
-        {
-            if (String.IsNullOrWhiteSpace(FilePath))
-                throw new InvalidOperationException("FilePath is empty");
-            if (String.IsNullOrWhiteSpace(TempPath))
-                throw new InvalidOperationException("TempPath is empty");
-            Contract.EndContractBlock();
-
-            //Copy the file to a temporary location. Changes will be made to the
-            //temporary file, then it will replace the original file
-            if (File.Exists(FilePath))
-                File.Copy(FilePath, TempPath, true);
-
-            //Set the size of the file to the size specified in the treehash
-            //This will also create an empty file if the file doesn't exist                        
-            
-            
-            SetFileSize(TempPath, ServerHash.Bytes);
-
-            //Update the temporary file with the data from the blocks
-            using (var stream = File.OpenWrite(TempPath))
-            {
-                foreach (var block in _blocks)
-                {
-                    var blockPath = block.Value;
-                    var blockIndex = block.Key;
-                    using (var blockStream = File.OpenRead(blockPath))
-                    {                        
-                        long offset = blockIndex*ServerHash.BlockSize;
-                        stream.Seek(offset, SeekOrigin.Begin);
-                        blockStream.CopyTo(stream);
-                    }
-                }
-            }
-            SwapFiles();
-
-            ClearBlocks();
-        }
-
-        private void SwapFiles()
-        {
-            if (String.IsNullOrWhiteSpace(FilePath))
-                throw new InvalidOperationException("FilePath is empty");
-            if (String.IsNullOrWhiteSpace(TempPath))
-                throw new InvalidOperationException("TempPath is empty");            
-            Contract.EndContractBlock();
-
-            if (File.Exists(FilePath))
-                File.Replace(TempPath, FilePath, null, true);
-            else
-            {
-                var targetDirectory = Path.GetDirectoryName(FilePath);
-                if (!Directory.Exists(targetDirectory))
-                    Directory.CreateDirectory(targetDirectory);
-                File.Move(TempPath, FilePath);
-            }
-        }
-
-        private void ClearBlocks()
-        {
-            if (Log.IsDebugEnabled)
-                Log.DebugFormat("Clearing blocks for {0}",this.FilePath);
-            //Get all the the block paths, orphan or not
-            var paths= _blocks.Select(pair => pair.Value)
-                          .Union(_orphanBlocks.Select(pair => pair.Value));
-            foreach (var filePath in paths)
-            {
-                File.Delete(filePath);
-            }
-
-            File.Delete(TempPath);
-            _blocks.Clear();
-            _orphanBlocks.Clear();
-        }
-
-        //Change the file's size, possibly truncating or adding to it
-        private  void SetFileSize(string filePath, long fileSize)
-        {
-            if (String.IsNullOrWhiteSpace(filePath))
-                throw new ArgumentNullException("filePath");
-            if (!Path.IsPathRooted(filePath))
-                throw new ArgumentException("The filePath must be rooted", "filePath");
-            if (fileSize < 0)
-                throw new ArgumentOutOfRangeException("fileSize");
-            Contract.EndContractBlock();
-
-            using (var stream = File.Open(filePath, FileMode.OpenOrCreate, FileAccess.Write))
-            {
-                stream.SetLength(fileSize);
-            }
-        }
-
-       /* //Check whether we should copy the local file to a temp path        
-        private  bool ShouldCopy(string localPath, string tempPath)
-        {
-            //No need to copy if there is no file
-            if (!File.Exists(localPath))
-                return false;
-
-            //If there is no temp file, go ahead and copy
-            if (!File.Exists(tempPath))
-                return true;
-
-            //If there is a temp file and is newer than the actual file, don't copy
-            var localLastWrite = File.GetLastWriteTime(localPath);
-            var tempLastWrite = File.GetLastWriteTime(tempPath);
-
-            //This could mean there is an interrupted download in progress
-            return (tempLastWrite < localLastWrite);
-        }*/
-
-
-        public bool UseOrphan(long blockIndex, string blockHash)
-        {
-            string blockPath=null;
-            if (_orphanBlocks.TryGetValue(blockHash,out blockPath))
-            {
-                _blocks[blockIndex] = blockPath;
-                return true;
-            }
-            return false;
-        }
-
-        public Task StoreBlock(long blockIndex,byte[] buffer)
-        {
-            var blockPath = String.Format("{0}.{1:000000}", TempPath, blockIndex);
-            _blocks[blockIndex] = blockPath;
-            //Remove any orphan files
-            if (File.Exists(blockPath))
-                File.Delete(blockPath);
-
-            return FileAsync.WriteAllBytes(blockPath, buffer);
-        }
-
-       
-
-    }
-}
+#region\r
+/* -----------------------------------------------------------------------\r
+ * <copyright file="BlockUpdater.cs" company="GRNet">\r
+ * \r
+ * Copyright 2011-2012 GRNET S.A. All rights reserved.\r
+ *\r
+ * Redistribution and use in source and binary forms, with or\r
+ * without modification, are permitted provided that the following\r
+ * conditions are met:\r
+ *\r
+ *   1. Redistributions of source code must retain the above\r
+ *      copyright notice, this list of conditions and the following\r
+ *      disclaimer.\r
+ *\r
+ *   2. Redistributions in binary form must reproduce the above\r
+ *      copyright notice, this list of conditions and the following\r
+ *      disclaimer in the documentation and/or other materials\r
+ *      provided with the distribution.\r
+ *\r
+ *\r
+ * THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS\r
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\r
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\r
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR\r
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\r
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\r
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF\r
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED\r
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\r
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN\r
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\r
+ * POSSIBILITY OF SUCH DAMAGE.\r
+ *\r
+ * The views and conclusions contained in the software and\r
+ * documentation are those of the authors and should not be\r
+ * interpreted as representing official policies, either expressed\r
+ * or implied, of GRNET S.A.\r
+ * </copyright>\r
+ * -----------------------------------------------------------------------\r
+ */\r
+#endregion\r
+using System;\r
+using System.Collections.Concurrent;\r
+using System.Diagnostics.Contracts;\r
+using System.IO;\r
+using System.Linq;\r
+using System.Reflection;\r
+using System.Threading.Tasks;\r
+using OpenSSL.Crypto;\r
+using Pithos.Network;\r
+\r
+namespace Pithos.Core.Agents\r
+{\r
+    class BlockUpdater\r
+    {\r
+        //TODO: Must clean orphaned blocks from the Cache folder.\r
+        //\r
+        //The Cache folder may have orphaned blocks. Blocks may be left in the Cache folder because:\r
+        //1. A download was in progress when the application terminated. These blocks are needed to proceed \r
+        //  with partial download\r
+        //2. The application terminated abnormally before the blocks were cleared after a download\r
+        //3. The server file was deleted before the download completed.\r
+        //\r
+        //In #1, we need to keep the blocks. We need to detect the other cases and delete orphans\r
+        //\r
+        //Mitigations:\r
+        // - Delete blocks with no corresponding state\r
+        // - Check and delete possible orphans when a Deletion is detected\r
+        // - Add Advanced command "Clear Cache"\r
+        //\r
+        //Need a better way to differentiate between cases #2, #3 and #1\r
+\r
+        private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);\r
+\r
+        public string FilePath { get; private set; }\r
+        public string RelativePath { get; private set; }\r
+\r
+        public string CachePath { get; private set; }\r
+\r
+        public TreeHash ServerHash { get; private set; }\r
+\r
+        public string TempPath { get; private set; }\r
+\r
+        public bool HasBlocks\r
+        {\r
+            get { return _blocks.Count>0; }            \r
+        }\r
+\r
+        readonly ConcurrentDictionary<long, string> _blocks = new ConcurrentDictionary<long, string>();\r
+        readonly ConcurrentDictionary<string, string> _orphanBlocks = new ConcurrentDictionary<string, string>();\r
+\r
+        [ContractInvariantMethod]\r
+        private void Invariants()\r
+        {\r
+            Contract.Invariant(Path.IsPathRooted(CachePath));\r
+            Contract.Invariant(Path.IsPathRooted(FilePath));\r
+            Contract.Invariant(Path.IsPathRooted(TempPath));\r
+            Contract.Invariant(!Path.IsPathRooted(RelativePath));\r
+            Contract.Invariant(_blocks!=null);\r
+            Contract.Invariant(_orphanBlocks!=null);\r
+            Contract.Invariant(ServerHash!=null);\r
+        }\r
+\r
+        public BlockUpdater(string cachePath, string filePath, string relativePath,TreeHash serverHash)\r
+        {   \r
+            if (String.IsNullOrWhiteSpace(cachePath))\r
+                throw new ArgumentNullException("cachePath");\r
+            if (!Path.IsPathRooted(cachePath))\r
+                throw new ArgumentException("The cachePath must be rooted", "cachePath");\r
+            \r
+            if (string.IsNullOrWhiteSpace(filePath))\r
+                throw new ArgumentNullException("filePath");\r
+            if (!Path.IsPathRooted(filePath))\r
+                throw new ArgumentException("The filePath must be rooted", "filePath");\r
+            \r
+            if (string.IsNullOrWhiteSpace(relativePath))\r
+                throw new ArgumentNullException("relativePath");\r
+            if (Path.IsPathRooted(relativePath))\r
+                throw new ArgumentException("The relativePath must NOT be rooted", "relativePath");\r
+\r
+            if (serverHash == null)\r
+                throw new ArgumentNullException("serverHash");\r
+            Contract.EndContractBlock();\r
+\r
+            CachePath=cachePath;\r
+            FilePath = filePath;\r
+            RelativePath=relativePath;\r
+            ServerHash = serverHash;\r
+            //The file will be stored in a temporary location while downloading with an extension .download\r
+            TempPath = Path.Combine(CachePath, RelativePath + ".download");\r
+            \r
+            //Need to calculate the directory path because RelativePath may include folders\r
+            var directoryPath = Path.GetDirectoryName(TempPath);            \r
+            //directoryPath CAN be null if TempPath is a root path\r
+            if (String.IsNullOrWhiteSpace(directoryPath))\r
+                throw new ArgumentException("TempPath");\r
+            //CachePath was absolute so directoryPath is absolute too\r
+            Contract.Assume(Path.IsPathRooted(directoryPath));\r
+            \r
+            if (!Directory.Exists(directoryPath))\r
+                Directory.CreateDirectory(directoryPath);\r
+\r
+            LoadOrphans(directoryPath);\r
+        }\r
+\r
+        private void LoadOrphans(string directoryPath)\r
+        {\r
+            if (string.IsNullOrWhiteSpace(directoryPath))\r
+                throw new ArgumentNullException("directoryPath");\r
+            if (!Path.IsPathRooted(directoryPath))\r
+                throw new ArgumentException("The directoryPath must be rooted", "directoryPath");\r
+            if (ServerHash==null)\r
+                throw new InvalidOperationException("ServerHash wasn't initialized");\r
+            Contract.EndContractBlock();\r
+\r
+            var fileNamename = Path.GetFileName(FilePath);\r
+            var orphans = Directory.GetFiles(directoryPath, fileNamename + ".*");\r
+            foreach (var orphan in orphans)\r
+            {\r
+                using (var hasher = new MessageDigestContext(MessageDigest.CreateByName(ServerHash.BlockHash)))                \r
+                {\r
+                    hasher.Init();\r
+                    var buffer=File.ReadAllBytes(orphan);\r
+                    //The server truncates nulls before calculating hashes, have to do the same\r
+                    //Find the last non-null byte, starting from the end\r
+                    var lastByteIndex = Array.FindLastIndex(buffer, buffer.Length-1, aByte => aByte != 0);\r
+                    //lastByteIndex may be -1 if the file was empty. We don't want to use that block file\r
+                    if (lastByteIndex >= 0)\r
+                    {\r
+                        byte[] block;\r
+                        if (lastByteIndex == buffer.Length - 1)\r
+                            block = buffer;\r
+                        else\r
+                        {\r
+                            block=new byte[lastByteIndex];\r
+                            Buffer.BlockCopy(buffer,0,block,0,lastByteIndex);\r
+                        }\r
+                        var binHash = hasher.Digest(block);\r
+                        var hash = binHash.ToHashString();\r
+                        _orphanBlocks[hash] = orphan;\r
+                    }\r
+                }\r
+            }\r
+        }\r
+\r
+\r
+        public void Commit()\r
+        {\r
+            if (String.IsNullOrWhiteSpace(FilePath))\r
+                throw new InvalidOperationException("FilePath is empty");\r
+            if (String.IsNullOrWhiteSpace(TempPath))\r
+                throw new InvalidOperationException("TempPath is empty");\r
+            Contract.EndContractBlock();\r
+\r
+            //Copy the file to a temporary location. Changes will be made to the\r
+            //temporary file, then it will replace the original file\r
+            if (File.Exists(FilePath))\r
+                File.Copy(FilePath, TempPath, true);\r
+\r
+            //Set the size of the file to the size specified in the treehash\r
+            //This will also create an empty file if the file doesn't exist                        \r
+            \r
+            \r
+            SetFileSize(TempPath, ServerHash.Bytes);\r
+\r
+            //Update the temporary file with the data from the blocks\r
+            using (var stream = File.OpenWrite(TempPath))\r
+            {\r
+                foreach (var block in _blocks)\r
+                {\r
+                    var blockPath = block.Value;\r
+                    var blockIndex = block.Key;\r
+                    using (var blockStream = File.OpenRead(blockPath))\r
+                    {                        \r
+                        long offset = blockIndex*ServerHash.BlockSize;\r
+                        stream.Seek(offset, SeekOrigin.Begin);\r
+                        blockStream.CopyTo(stream);\r
+                    }\r
+                }\r
+            }\r
+            SwapFiles();\r
+\r
+            ClearBlocks();\r
+        }\r
+\r
+        private void SwapFiles()\r
+        {\r
+            if (String.IsNullOrWhiteSpace(FilePath))\r
+                throw new InvalidOperationException("FilePath is empty");\r
+            if (String.IsNullOrWhiteSpace(TempPath))\r
+                throw new InvalidOperationException("TempPath is empty");            \r
+            Contract.EndContractBlock();\r
+\r
+            if (File.Exists(FilePath))\r
+                File.Replace(TempPath, FilePath, null, true);\r
+            else\r
+            {\r
+                var targetDirectory = Path.GetDirectoryName(FilePath);\r
+                if (!Directory.Exists(targetDirectory))\r
+                    Directory.CreateDirectory(targetDirectory);\r
+                File.Move(TempPath, FilePath);\r
+            }\r
+        }\r
+\r
+        private void ClearBlocks()\r
+        {\r
+            if (Log.IsDebugEnabled)\r
+                Log.DebugFormat("Clearing blocks for {0}",this.FilePath);\r
+            //Get all the the block paths, orphan or not\r
+            var paths= _blocks.Select(pair => pair.Value)\r
+                          .Union(_orphanBlocks.Select(pair => pair.Value));\r
+            foreach (var filePath in paths)\r
+            {\r
+                File.Delete(filePath);\r
+            }\r
+\r
+            File.Delete(TempPath);\r
+            _blocks.Clear();\r
+            _orphanBlocks.Clear();\r
+        }\r
+\r
+        //Change the file's size, possibly truncating or adding to it\r
+        private  void SetFileSize(string filePath, long fileSize)\r
+        {\r
+            if (String.IsNullOrWhiteSpace(filePath))\r
+                throw new ArgumentNullException("filePath");\r
+            if (!Path.IsPathRooted(filePath))\r
+                throw new ArgumentException("The filePath must be rooted", "filePath");\r
+            if (fileSize < 0)\r
+                throw new ArgumentOutOfRangeException("fileSize");\r
+            Contract.EndContractBlock();\r
+\r
+            using (var stream = File.Open(filePath, FileMode.OpenOrCreate, FileAccess.Write))\r
+            {\r
+                stream.SetLength(fileSize);\r
+            }\r
+        }\r
+\r
+       /* //Check whether we should copy the local file to a temp path        \r
+        private  bool ShouldCopy(string localPath, string tempPath)\r
+        {\r
+            //No need to copy if there is no file\r
+            if (!File.Exists(localPath))\r
+                return false;\r
+\r
+            //If there is no temp file, go ahead and copy\r
+            if (!File.Exists(tempPath))\r
+                return true;\r
+\r
+            //If there is a temp file and is newer than the actual file, don't copy\r
+            var localLastWrite = File.GetLastWriteTime(localPath);\r
+            var tempLastWrite = File.GetLastWriteTime(tempPath);\r
+\r
+            //This could mean there is an interrupted download in progress\r
+            return (tempLastWrite < localLastWrite);\r
+        }*/\r
+\r
+\r
+        public bool UseOrphan(long blockIndex, string blockHash)\r
+        {\r
+            string blockPath=null;\r
+            if (_orphanBlocks.TryGetValue(blockHash,out blockPath))\r
+            {\r
+                _blocks[blockIndex] = blockPath;\r
+                return true;\r
+            }\r
+            return false;\r
+        }\r
+\r
+        public Task StoreBlock(long blockIndex,byte[] buffer)\r
+        {\r
+            var blockPath = String.Format("{0}.{1:000000}", TempPath, blockIndex);\r
+            _blocks[blockIndex] = blockPath;\r
+            //Remove any orphan files\r
+            if (File.Exists(blockPath))\r
+                File.Delete(blockPath);\r
+\r
+            return FileAsync.WriteAllBytes(blockPath, buffer);\r
+        }\r
+\r
+       \r
+\r
+    }\r
+}\r