2 /* -----------------------------------------------------------------------
\r
3 * <copyright file="BlockExtensions.cs" company="GRNet">
\r
5 * Copyright 2011-2012 GRNET S.A. All rights reserved.
\r
7 * Redistribution and use in source and binary forms, with or
\r
8 * without modification, are permitted provided that the following
\r
9 * conditions are met:
\r
11 * 1. Redistributions of source code must retain the above
\r
12 * copyright notice, this list of conditions and the following
\r
15 * 2. Redistributions in binary form must reproduce the above
\r
16 * copyright notice, this list of conditions and the following
\r
17 * disclaimer in the documentation and/or other materials
\r
18 * provided with the distribution.
\r
21 * THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
\r
22 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
\r
23 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
\r
24 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
\r
25 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
\r
26 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
\r
27 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
\r
28 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
\r
29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
\r
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
\r
31 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
\r
32 * POSSIBILITY OF SUCH DAMAGE.
\r
34 * The views and conclusions contained in the software and
\r
35 * documentation are those of the authors and should not be
\r
36 * interpreted as representing official policies, either expressed
\r
37 * or implied, of GRNET S.A.
\r
39 * -----------------------------------------------------------------------
\r
43 using System.Collections.Generic;
\r
44 using System.Diagnostics;
\r
45 using System.Diagnostics.Contracts;
\r
47 using System.Reflection;
\r
48 using System.Security.Cryptography;
\r
51 using System.Text.RegularExpressions;
\r
52 using System.Threading;
\r
53 using System.Threading.Tasks;
\r
54 using Pithos.Network;
\r
57 namespace Pithos.Core.Agents
\r
59 static class BlockExtensions
\r
62 private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
\r
64 public static int Read(this FileInfo fileInfo,byte[] buffer,long offset,int count)
\r
67 throw new ArgumentOutOfRangeException("offset", offset, "The file offset can't be negative");
\r
68 Contract.EndContractBlock();
\r
69 //Open the stream only long enough to read a block
\r
70 using (var stream = fileInfo.OpenRead())
\r
72 stream.Seek(offset, SeekOrigin.Begin);
\r
73 return stream.Read(buffer, 0, count);
\r
78 public static string CalculateHash(this FileSystemInfo info,int blockSize,string algorithm,CancellationToken token,IProgress<double> progress )
\r
81 throw new ArgumentNullException("info");
\r
82 if (String.IsNullOrWhiteSpace(info.FullName))
\r
83 throw new ArgumentException("info");
\r
85 throw new ArgumentOutOfRangeException("blockSize",blockSize,"blockSize must be greater than 0");
\r
86 if (String.IsNullOrWhiteSpace(algorithm))
\r
87 throw new ArgumentNullException("algorithm");
\r
88 Contract.EndContractBlock();
\r
90 if (info.FullName.Split('/').Contains(".pithos.cache"))
\r
91 throw new ArgumentException(String.Format("Trying to hash file from the cache folder: [{0}]", info.FullName));
\r
93 //The hash for directories is an empty string
\r
94 if (info is DirectoryInfo)
\r
95 return String.Empty;
\r
96 //The hash for non-existent files is an empty string
\r
98 return String.Empty;
\r
100 return Signature.CalculateTreeHash(info.FullName, blockSize, algorithm,token,progress).TopHash.ToHashString();
\r
105 ///Calculates a simple hash for an entire file
\r
107 /// <param name="info">The file to hash</param>
\r
108 /// <param name="hasher">The hash algorithm to use</param>
\r
109 /// <returns>A hash value for the entire file. An empty string if the file does not exist.</returns>
\r
110 public static string ComputeShortHash(this FileInfo info, HashAlgorithm hasher,IStatusNotification notification)
\r
113 throw new ArgumentNullException("info");
\r
115 throw new ArgumentNullException("hasher");
\r
116 Contract.EndContractBlock();
\r
120 return String.Empty;
\r
121 if (info.FullName.Split('/').Contains(".pithos.cache"))
\r
122 throw new ArgumentException(String.Format("Trying to hash file from the cache folder: [{0}]", info.FullName));
\r
124 if (Log.IsDebugEnabled)
\r
125 Log.DebugFormat("Short Hashing [{0}] ",info.FullName);
\r
127 if (info.Length==0)
\r
128 return Signature.MD5_EMPTY;
\r
130 var progress = new StatusNotification("");
\r
132 using (var stream = new FileStream(info.FullName,FileMode.Open, FileAccess.Read, FileShare.Read,Signature.BufferSize))
\r
134 var buffer = new byte[65536];
\r
139 bytesRead = stream.Read(buffer, 0, 32768);
\r
142 hasher.TransformBlock(buffer, 0, bytesRead, null, 0);
\r
145 if (counter % 100 == 0)
\r
147 progress.Title = String.Format("Hashing {0:p} of {1}", stream.Position*1.0/stream.Length,
\r
149 notification.Notify(progress);
\r
151 } while (bytesRead > 0);
\r
152 hasher.TransformFinalBlock(buffer, 0, 0);
\r
153 var hash = hasher.Hash;
\r
155 progress.Title = String.Format("Hashed {0} ", info.Name);
\r
156 notification.Notify(progress);
\r
158 var hashString = hash.ToHashString();
\r
164 public static async Task<string> ComputeShortHash(this FileInfo info, MD5BlockCalculator calculator,IStatusNotification notification)
\r
167 throw new ArgumentNullException("info");
\r
168 if(calculator== null)
\r
169 throw new ArgumentNullException("calculator");
\r
170 Contract.EndContractBlock();
\r
173 return String.Empty;
\r
175 if (info.FullName.Split('/').Contains(".pithos.cache"))
\r
176 throw new ArgumentException(String.Format("Trying to hash file from the cache folder: [{0}]", info.FullName));
\r
178 if (Log.IsDebugEnabled)
\r
179 Log.DebugFormat("Short Hashing [{0}] ",info.FullName);
\r
181 if (info.Length == 0)
\r
182 return Signature.MD5_EMPTY;
\r
184 var progress = new StatusNotification("");
\r
188 using (var stream = new FileStream(info.FullName,FileMode.Open, FileAccess.Read, FileShare.Read,Signature.BufferSize))
\r
194 var buffer = new byte[65536];
\r
195 bytesRead = stream.Read(buffer, 0, 32768);
\r
198 calculator.PostBlock(counter,buffer,bytesRead);
\r
201 if (counter % 100 == 0)
\r
203 progress.Title = String.Format("Hashing {0:p} of {1}", stream.Position*1.0/stream.Length,
\r
205 notification.Notify(progress);
\r
207 } while (bytesRead > 0);
\r
209 var hashString = await calculator.GetHash();
\r
212 progress.Title = String.Format("Hashed {0} ", info.Name);
\r
213 notification.Notify(progress);
\r
220 public static string ComputeShortHash(this FileInfo info,IStatusNotification notification)
\r
223 throw new ArgumentNullException("info");
\r
224 Contract.EndContractBlock();
\r
225 if (info.FullName.Split('/').Contains(".pithos.cache"))
\r
226 throw new ArgumentException(String.Format("Trying to hash file from the cache folder: [{0}]", info.FullName));
\r
228 using (var hasher=HashAlgorithm.Create("md5"))
\r
230 return ComputeShortHash(info,hasher,notification);
\r