using System;\r
-using System.Collections.Generic;\r
using System.ComponentModel.Composition;\r
using System.Diagnostics.Contracts;\r
using System.IO;\r
-using System.Linq;\r
using System.Reflection;\r
using System.Threading;\r
using System.Threading.Tasks;\r
[Import]\r
private IStatusKeeper StatusKeeper { get; set; }\r
\r
+ [Import]\r
+ private IPithosSettings Settings { get; set; }\r
\r
public IStatusNotification StatusNotification { get; set; }\r
\r
\r
\r
//Download a file.\r
- public async Task DownloadCloudFile(AccountInfo accountInfo, ObjectInfo cloudFile, string filePath,TreeHash localTreeHash,CancellationToken cancellationToken)\r
+ public async Task<TreeHash> DownloadCloudFile(AccountInfo accountInfo, ObjectInfo cloudFile, string filePath,CancellationToken cancellationToken)\r
{\r
if (accountInfo == null)\r
throw new ArgumentNullException("accountInfo");\r
throw new ArgumentNullException("cloudFile");\r
if (String.IsNullOrWhiteSpace(cloudFile.Account))\r
throw new ArgumentNullException("cloudFile");\r
- if (String.IsNullOrWhiteSpace(cloudFile.Container))\r
+ if (cloudFile.Container==null)\r
+ throw new ArgumentNullException("cloudFile");\r
+ if (cloudFile.Container.IsAbsoluteUri)\r
throw new ArgumentNullException("cloudFile");\r
if (String.IsNullOrWhiteSpace(filePath))\r
throw new ArgumentNullException("filePath");\r
{\r
// var cancellationToken=_cts.Token;// .ThrowIfCancellationRequested();\r
\r
- if (await WaitOrAbort(accountInfo,cloudFile, cancellationToken))\r
- return;\r
+ //The file's treehash after download completes. For directories, the treehash is always the empty hash\r
+ var finalHash = TreeHash.Empty;\r
+\r
+ if (await WaitOrAbort(accountInfo,cloudFile, cancellationToken).ConfigureAwait(false))\r
+ return finalHash;\r
\r
+ var fileName = Path.GetFileName(filePath);\r
\r
- var localPath = FileInfoExtensions.GetProperFilePathCapitalization(filePath);\r
- var relativeUrl = new Uri(cloudFile.Name, UriKind.Relative);\r
+ var info = FileInfoExtensions.FromPath(filePath).WithProperCapitalization();\r
+\r
+ TreeHash localTreeHash;\r
+\r
+ using (StatusNotification.GetNotifier("Hashing for Download {0}", "Hashed for Download {0}", fileName))\r
+ {\r
+ var state = StatusKeeper.GetStateByFilePath(filePath);\r
+ var progress = new Progress<double>(d =>\r
+ StatusNotification.Notify(new StatusNotification(String.Format("Hashing for Download {0} of {1}", d, fileName))));\r
+\r
+ localTreeHash = StatusAgent.CalculateTreeHash(info, accountInfo, state, Settings.HashingParallelism, cancellationToken, progress);\r
+ }\r
+ \r
+ var localPath = info.FullName;\r
+ var relativeUrl = cloudFile.Name;\r
\r
var url = relativeUrl.ToString();\r
- if (cloudFile.Name.EndsWith(".ignore", StringComparison.InvariantCultureIgnoreCase))\r
- return;\r
+ if (url.EndsWith(".ignore", StringComparison.InvariantCultureIgnoreCase))\r
+ return finalHash;\r
\r
if (!Selectives.IsSelected(accountInfo,cloudFile))\r
- return;\r
+ return finalHash;\r
\r
\r
//Are we already downloading or uploading the file? \r
using (var gate = NetworkGate.Acquire(localPath, NetworkOperation.Downloading))\r
{\r
if (gate.Failed)\r
- return;\r
+ return finalHash;\r
\r
var client = new CloudFilesClient(accountInfo);\r
var account = cloudFile.Account;\r
}\r
else\r
{\r
- var isChanged = IsObjectChanged(cloudFile, localPath);\r
+ var isChanged = IsObjectChanged(cloudFile, localPath,localTreeHash);\r
if (isChanged)\r
{\r
//Retrieve the hashmap from the server\r
- var serverHash = await client.GetHashMap(account, container, url);\r
+ var serverHash = await client.GetHashMap(account, container, relativeUrl).ConfigureAwait(false);\r
//If it's a small file\r
if (serverHash.Hashes.Count == 1)\r
//Download it in one go\r
var attributes = File.GetAttributes(localPath);\r
File.SetAttributes(localPath, attributes | FileAttributes.ReadOnly);\r
}\r
+\r
+ //Once download completes, the final hash will be equal to the server hash\r
+ finalHash = serverHash;\r
+\r
}\r
}\r
\r
//Now we can store the object's metadata without worrying about ghost status entries\r
- StatusKeeper.StoreInfo(localPath, cloudFile);\r
+ StatusKeeper.StoreInfo(localPath, cloudFile,finalHash);\r
\r
}\r
+ return finalHash;\r
}\r
\r
}\r
throw new ArgumentException("cloudFile is a directory, not a file", "cloudFile");\r
Contract.EndContractBlock();\r
\r
- if (await WaitOrAbort(accountInfo,cloudFile, cancellationToken))\r
+ if (await WaitOrAbort(accountInfo, cloudFile, cancellationToken).ConfigureAwait(false))\r
return;\r
\r
var fileAgent = GetFileAgent(accountInfo);\r
StatusNotification.SetPithosStatus(PithosStatus.LocalSyncing, String.Format("Calculating hashmap for {0} before download", Path.GetFileName(localPath)));\r
//Calculate the file's treehash\r
\r
- //TODO: Should pass cancellation token here\r
- var treeHash = localTreeHash ?? Signature.CalculateTreeHashAsync(localPath, (int)serverHash.BlockSize, serverHash.BlockHash, 2);\r
+ var fileName = Path.GetFileName(localPath);\r
+ var progress = new Progress<double>(d =>\r
+ StatusNotification.Notify(new StatusNotification(String.Format("Hashing for Download {0} of {1}", d, fileName))));\r
+ \r
+ var treeHash = localTreeHash ?? Signature.CalculateTreeHashAsync(localPath, (int)serverHash.BlockSize, serverHash.BlockHash, Settings.HashingParallelism,cancellationToken,progress);\r
\r
//And compare it with the server's hash\r
var upHashes = serverHash.GetHashesAsStrings();\r
ReportDownloadProgress(Path.GetFileName(localPath), 0,0, upHashes.Length, cloudFile.Bytes);\r
\r
\r
- int i = 0;\r
+ long i = 0;\r
client.DownloadProgressChanged += (sender, args) =>\r
ReportDownloadProgress(Path.GetFileName(localPath), i, args.ProgressPercentage, upHashes.Length, cloudFile.Bytes);\r
\r
\r
for (i = 0; i < upHashes.Length; i++)\r
{\r
- if (await WaitOrAbort(accountInfo,cloudFile, cancellationToken))\r
+ if (await WaitOrAbort(accountInfo, cloudFile, cancellationToken).ConfigureAwait(false))\r
return;\r
\r
//For every non-matching hash\r
continue;\r
}\r
Log.InfoFormat("[BLOCK GET] START {0} of {1} for {2}", i, upHashes.Length, localPath);\r
- var start = i * serverHash.BlockSize;\r
+ long start = i * serverHash.BlockSize;\r
//To download the last block just pass a null for the end of the range\r
long? end = null;\r
if (i < upHashes.Length - 1)\r
end = ((i + 1) * serverHash.BlockSize);\r
\r
- //TODO: Pass token here\r
+ \r
//Download the missing block\r
- var block = await client.GetBlock(cloudFile.Account, cloudFile.Container, relativeUrl, start, end,cancellationToken);\r
+ byte[] block = await client.GetBlock(cloudFile.Account, cloudFile.Container, relativeUrl, start, end, cancellationToken).ConfigureAwait(false);\r
\r
//and store it\r
blockUpdater.StoreBlock(i, block);\r
StatusNotification.NotifyChangedFile(localPath);\r
\r
Log.InfoFormat("[BLOCK GET] COMPLETE {0}", localPath);\r
+ \r
}\r
\r
//Download a small file with a single GET operation\r
throw new ArgumentException("cloudFile is a directory, not a file", "cloudFile");\r
Contract.EndContractBlock();\r
\r
- if (await WaitOrAbort(accountInfo,cloudFile, cancellationToken))\r
+ if (await WaitOrAbort(accountInfo, cloudFile, cancellationToken).ConfigureAwait(false))\r
return;\r
\r
var localPath = FileInfoExtensions.GetProperFilePathCapitalization(filePath);\r
if (!Directory.Exists(tempFolder))\r
Directory.CreateDirectory(tempFolder);\r
\r
- //TODO: Should pass the token here\r
-\r
//Download the object to the temporary location\r
- await client.GetObject(cloudFile.Account, cloudFile.Container, relativeUrl.ToString(), tempPath,cancellationToken);\r
+ await client.GetObject(cloudFile.Account, cloudFile.Container, relativeUrl, tempPath, cancellationToken).ConfigureAwait(false);\r
\r
//Create the local folder if it doesn't exist (necessary for shared objects)\r
var localFolder = Path.GetDirectoryName(localPath);\r
}\r
\r
\r
- private void ReportDownloadProgress(string fileName, int block, int blockPercentage,int totalBlocks, long fileSize)\r
+ private void ReportDownloadProgress(string fileName, long block, int blockPercentage,int totalBlocks, long fileSize)\r
{\r
StatusNotification.Notify(totalBlocks == 0\r
? new ProgressNotification(fileName, "Downloading", 1, blockPercentage,1, fileSize)\r
: new ProgressNotification(fileName, "Downloading", block, blockPercentage, totalBlocks, fileSize));\r
}\r
\r
- private bool IsObjectChanged(ObjectInfo cloudFile, string localPath)\r
+ private bool IsObjectChanged(ObjectInfo cloudFile, string localPath,TreeHash localTreeHash)\r
{\r
//If the target is a directory, there are no changes to download\r
if (Directory.Exists(localPath))\r
if (localState == null)\r
return true;\r
\r
- var info = new FileInfo(localPath);\r
- var shortHash = info.ComputeShortHash();\r
+ var localHash= localTreeHash.TopHash.ToHashString();\r
//If the file is different from the stored state, we have a change\r
- if (localState.ShortHash != shortHash)\r
+ if (localState.Checksum != localHash)\r
return true;\r
//If the top hashes differ, we have a change\r
return (localState.Checksum != cloudFile.X_Object_Hash);\r
private async Task<bool> WaitOrAbort(AccountInfo account,ObjectInfo cloudFile, CancellationToken token)\r
{\r
token.ThrowIfCancellationRequested();\r
- await UnpauseEvent.WaitAsync();\r
+ await UnpauseEvent.WaitAsync().ConfigureAwait(false);\r
var shouldAbort = !Selectives.IsSelected(account,cloudFile);\r
if (shouldAbort)\r
Log.InfoFormat("Aborting [{0}]", cloudFile.Uri);\r