#endregion
using System.Diagnostics.Contracts;
using System.IO;
+using System.Reflection;
using System.Threading.Tasks;
using Castle.ActiveRecord;
using Castle.ActiveRecord.Framework;
+using Castle.ActiveRecord.Queries;
+using NHibernate.Criterion;
using Pithos.Core.Agents;
using Pithos.Interfaces;
using Pithos.Network;
namespace Pithos.Core
{
using System;
- using System.Collections.Generic;
- using System.Linq;
+ using System.Collections.Generic;
/// <summary>
/// TODO: Update summary.
[ActiveRecord]
public class FileState : ActiveRecordLinqBase<FileState>
{
- private static readonly ILog Log = LogManager.GetLogger("FileState");
+ private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
+
private IList<FileTag> _tags = new List<FileTag>();
public Guid Id { get; set; }
+ //[Property(Unique = true, UniqueKey = "IX_FileState_ObjectID")]
+ [Property]
+ public string ObjectID { get; set; }
+
[Property(Unique = true, UniqueKey = "IX_FileState_FilePath")]
public string FilePath { get; set; }
public FileStatus FileStatus { get; set; }
[Property]
- public string Checksum { get; set; }
+ public string ConflictReason { get; set; }
+
+ private string _checksum;
+
+ /// <summary>
+ /// The tophash value of the file, calculated by using a Merkle hash with the SHA256 algorithm
+ /// </summary>
+ /// <remarks>
+ /// The SHA256 algorithm is substantially more expenive than other algorithms.
+ /// Recalculating the Checksum should be avoided whenever possible.
+ /// </remarks>
+ [Property]
+ public string Checksum
+ {
+ get
+ {
+ return _checksum;
+ }
+ set
+ {
+ _checksum = value;
+ }
+ }
+
+ private string _shortHash;
+
+ /// <summary>
+ /// An easy to calcualte hash over the entire file, used to detect file changes
+ /// </summary>
+ /// <remarks>The algorithm used to calculate this hash should be cheap</remarks>
+ [Property(NotNull=true,Default="")]
+ public string ShortHash
+ {
+ get
+ {
+ return _shortHash;
+ }
+ set
+ {
+ _shortHash = value;
+ }
+ }
[Property]
}
- public static void StoreOverlayStatus(string absolutePath, FileOverlayStatus newStatus)
+ /*public static void StoreOverlayStatus(string absolutePath, FileOverlayStatus newStatus)
+ {
+ if (string.IsNullOrWhiteSpace(absolutePath))
+ throw new ArgumentNullException("absolutePath");
+ Contract.EndContractBlock();
+
+ ExecuteWithRetry((session, instance) =>
+ {
+ const string hqlUpdate =
+ "update FileState set OverlayStatus= :status where FilePath = :path ";
+ var updatedEntities = session.CreateQuery(hqlUpdate)
+ .SetString("path", absolutePath)
+ .SetEnum("status", newStatus)
+ .ExecuteUpdate();
+ if (updatedEntities == 0)
+ {
+ var newState = new FileState
+ {
+ FilePath = absolutePath,
+ Id = Guid.NewGuid(),
+ OverlayStatus = newStatus,
+ ShortHash = String.Empty,
+ IsFolder=Directory.Exists(absolutePath)
+ };
+ newState.CreateAndFlush();
+ }
+ return null;
+ }, null);
+
+ }
+*/
+ public static void StoreOverlayStatus(string absolutePath, FileOverlayStatus newStatus,string shortHash)
{
if (string.IsNullOrWhiteSpace(absolutePath))
throw new ArgumentNullException("absolutePath");
FilePath = absolutePath,
Id = Guid.NewGuid(),
OverlayStatus = newStatus,
+ ShortHash = shortHash??String.Empty,
IsFolder=Directory.Exists(absolutePath)
};
newState.CreateAndFlush();
}, null);
}*/
- public static void UpdateChecksum(string absolutePath, string checksum)
+ public static void UpdateChecksum(string absolutePath, string shortHash, string checksum)
{
if (string.IsNullOrWhiteSpace(absolutePath))
throw new ArgumentNullException("absolutePath");
ExecuteWithRetry((session, instance) =>
{
- const string hqlUpdate = "update FileState set Checksum= :checksum where FilePath = :path ";
+ const string hqlUpdate = "update FileState set Checksum= :checksum,ShortHash=:shortHash where FilePath = :path ";
var updatedEntities = session.CreateQuery(hqlUpdate)
.SetString("path", absolutePath)
.SetString("checksum", checksum)
+ .SetString("shortHash", shortHash)
.ExecuteUpdate();
return updatedEntities;
}, null);
}, null);
}
- public static Task<FileState> CreateForAsync(string filePath, int blockSize, string algorithm)
+ public static FileState CreateFor(FileSystemInfo info,IStatusNotification notification)
{
- if (blockSize <= 0)
- throw new ArgumentOutOfRangeException("blockSize");
- if (String.IsNullOrWhiteSpace(algorithm))
- throw new ArgumentNullException("algorithm");
+ if(info==null)
+ throw new ArgumentNullException("info");
Contract.EndContractBlock();
-
-
+
+ if (info is DirectoryInfo)
+ return new FileState
+ {
+ FilePath = info.FullName,
+ OverlayStatus = FileOverlayStatus.Unversioned,
+ FileStatus = FileStatus.Created,
+ ShortHash=String.Empty,
+ Id = Guid.NewGuid()
+ };
+
+
+ var shortHash = ((FileInfo)info).ComputeShortHash(notification);
var fileState = new FileState
{
- FilePath = filePath,
+ FilePath = info.FullName,
OverlayStatus = FileOverlayStatus.Unversioned,
- FileStatus = FileStatus.Created,
+ FileStatus = FileStatus.Created,
+ ShortHash=shortHash,
Id = Guid.NewGuid()
};
-
-
- return fileState.UpdateHashesAsync(blockSize, algorithm);
+ return fileState;
}
- public async Task<FileState> UpdateHashesAsync(int blockSize, string algorithm)
- {
- if (blockSize <= 0)
- throw new ArgumentOutOfRangeException("blockSize");
- if (String.IsNullOrWhiteSpace(algorithm))
- throw new ArgumentNullException("algorithm");
- Contract.EndContractBlock();
-
- //Skip updating the hash for folders
- if (Directory.Exists(FilePath))
- return this;
-
- var hash = await TaskEx.Run(() =>
- {
- var info = new FileInfo(FilePath);
- return info.CalculateHash(blockSize, algorithm);
- });
-
- Checksum = hash;
-
- return this;
- }
private static void ExecuteWithRetry(NHibernateDelegate call, object state)
{
throw;
}
}
+
+ /// <summary>
+ /// Mark Unversioned all FileState rows from the database whose path
+ /// starts with one of the removed paths
+ /// </summary>
+ /// <param name="removed"></param>
+ public static void UnversionPaths(List<string> removed)
+ {
+ if (removed == null)
+ return;
+ if (removed.Count == 0)
+ return;
+
+ //Create a disjunction (list of OR statements
+ var disjunction = new Disjunction();
+ foreach (var path in removed)
+ {
+ //with the restriction FileState.FilePath like '@path%'
+ disjunction.Add(Restrictions.On<FileState>(s => s.FilePath)
+ .IsLike(path, MatchMode.Start));
+ }
+
+ //Generate a query from the disjunction
+ var query=QueryOver.Of<FileState>().Where(disjunction);
+
+ ExecuteWithRetry((session,instance)=>
+ {
+ using (var t=session.BeginTransaction())
+ {
+ var states = query.GetExecutableQueryOver(session).List();
+ foreach (var state in states)
+ {
+ state.FileStatus = FileStatus.Unversioned;
+ state.OverlayStatus = FileOverlayStatus.Unversioned;
+ state.Update();
+ }
+ t.Commit();
+ }
+ return null;
+ },null);
+ }
+
}
[ActiveRecord("Tags")]