using System.Diagnostics.Contracts;
using System.IO;
using System.Linq;
+using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using Pithos.Network;
public TreeHash ServerHash { get; private set; }
public string TempPath { get; private set; }
-
+
public BlockUpdater(string fragmentsPath, string filePath, string relativePath,TreeHash serverHash)
- {
+ {
+ if (String.IsNullOrWhiteSpace(fragmentsPath))
+ throw new ArgumentNullException("fragmentsPath");
+ if (!Path.IsPathRooted(fragmentsPath))
+ throw new ArgumentException("The fragmentsPath must be rooted", "fragmentsPath");
+
+ 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();
+
FragmentsPath=fragmentsPath;
FilePath = filePath;
RelativePath=relativePath;
ServerHash = serverHash;
-
- Start();
- }
-
- public void Start()
- {
//The file will be stored in a temporary location while downloading with an extension .download
TempPath = Path.Combine(FragmentsPath, RelativePath + ".download");
+
var directoryPath = Path.GetDirectoryName(TempPath);
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");
+ 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);
+ 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
- File.Copy(FilePath, TempPath, true);
+ 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
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
private void ClearBlocks()
{
- foreach (var block in _blocks)
+ //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)
{
- var filePath = block.Value;
File.Delete(filePath);
}
+
File.Delete(TempPath);
_blocks.Clear();
+ _orphanBlocks.Clear();
}
//Change the file's size, possibly truncating or adding to it
}*/
ConcurrentDictionary<int,string> _blocks=new ConcurrentDictionary<int, string>();
+ ConcurrentDictionary<string, string> _orphanBlocks = new ConcurrentDictionary<string, string>();
+
+ public bool UseOrphan(int blockIndex, string blockHash)
+ {
+ string blockPath=null;
+ if (_orphanBlocks.TryGetValue(blockHash,out blockPath))
+ {
+ _blocks[blockIndex] = blockPath;
+ return true;
+ }
+ return false;
+ }
public Task StoreBlock(int blockIndex,byte[] buffer)
{
- var blockPath = String.Format("{0}.{1:3}", TempPath, blockIndex);
+ 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);
}