X-Git-Url: https://code.grnet.gr/git/pithos-ms-client/blobdiff_plain/0af3141d74dcd7b7a75fcaa51414e52691c1f5fe..adfa4645c826a5adbbbf5a51459580e7191350c0:/trunk/Pithos.Core/Agents/BlockUpdater.cs diff --git a/trunk/Pithos.Core/Agents/BlockUpdater.cs b/trunk/Pithos.Core/Agents/BlockUpdater.cs index 6b0d310..3edc216 100644 --- a/trunk/Pithos.Core/Agents/BlockUpdater.cs +++ b/trunk/Pithos.Core/Agents/BlockUpdater.cs @@ -1,4 +1,45 @@ -using System; +#region +/* ----------------------------------------------------------------------- + * + * + * 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. + * + * ----------------------------------------------------------------------- + */ +#endregion +using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics.Contracts; @@ -16,19 +57,38 @@ namespace Pithos.Core.Agents public string FilePath { get; private set; } public string RelativePath { get; private set; } - public string FragmentsPath { 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 _blocks = new ConcurrentDictionary(); + readonly ConcurrentDictionary _orphanBlocks = new ConcurrentDictionary(); + + [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 fragmentsPath, string filePath, string relativePath,TreeHash serverHash) + public BlockUpdater(string cachePath, 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(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"); @@ -44,14 +104,21 @@ namespace Pithos.Core.Agents throw new ArgumentNullException("serverHash"); Contract.EndContractBlock(); - FragmentsPath=fragmentsPath; + 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(FragmentsPath, RelativePath + ".download"); - - var directoryPath = Path.GetDirectoryName(TempPath); + 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); @@ -64,6 +131,8 @@ namespace Pithos.Core.Agents 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); @@ -76,9 +145,13 @@ namespace Pithos.Core.Agents //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; + //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; + } } } } @@ -98,7 +171,9 @@ namespace Pithos.Core.Agents 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 + //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 @@ -132,7 +207,12 @@ namespace Pithos.Core.Agents 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() @@ -186,8 +266,6 @@ namespace Pithos.Core.Agents return (tempLastWrite < localLastWrite); }*/ - ConcurrentDictionary _blocks=new ConcurrentDictionary(); - ConcurrentDictionary _orphanBlocks = new ConcurrentDictionary(); public bool UseOrphan(int blockIndex, string blockHash) { @@ -211,21 +289,7 @@ namespace Pithos.Core.Agents return FileAsync.WriteAllBytes(blockPath, buffer); } - /*private Task WriteAsync(string filePath, byte[] buffer, int offset, int count) - { - var stream = FileAsync.OpenWrite(filePath); - try - { - stream.Seek(offset, SeekOrigin.Begin); - var write = stream.WriteAsync(buffer, 0, count); - return write.ContinueWith(s => stream.Close()); - } - catch (Exception ex) - { - stream.Close(); - return Task.Factory.FromException(ex); - } - }*/ + } }