-#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