Changes to hash calculation.
[pithos-ms-client] / trunk / Pithos.Core / FileState.cs
index 559d584..edfa1ff 100644 (file)
-#region
-/* -----------------------------------------------------------------------
- * <copyright file="FileState.cs" company="GRNet">
- * 
- * 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.
- * </copyright>
- * -----------------------------------------------------------------------
- */
-#endregion
-using System.Diagnostics.Contracts;
-using System.IO;
-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;
-using log4net;
-
-namespace Pithos.Core
-{
-    using System;
-    using System.Collections.Generic;    
-
-    /// <summary>
-    /// TODO: Update summary.
-    /// </summary>
-    [ActiveRecord]
-    public class FileState : ActiveRecordLinqBase<FileState>
-    {
-        private static readonly ILog Log = LogManager.GetLogger("FileState");
-
-        private IList<FileTag> _tags = new List<FileTag>();
-
-        [PrimaryKey(PrimaryKeyType.Guid)]
-        public Guid Id { get; set; }
-
-        
-        [Property(Unique = true, UniqueKey = "IX_FileState_FilePath")]
-        public string FilePath { get; set; }
-
-        [Property]
-        public FileOverlayStatus OverlayStatus { get; set; }
-
-        [Property]
-        public FileStatus FileStatus { get; set; }
-
-        [Property]
-        public string Checksum { get; set; }
-
-
-        [Property]
-        public long? Version { get; set; }
-
-        [Property]
-        public DateTime? VersionTimeStamp { get; set; }
-
-
-        [Property]
-        public bool IsShared { get; set; }
-
-        [Property]
-        public string SharedBy { get; set; }
-
-        [Property]
-        public bool ShareWrite { get; set; }
-
-        [Property]
-        public bool IsFolder{ get; set; }
-
-        [HasMany(Cascade = ManyRelationCascadeEnum.AllDeleteOrphan, Lazy = true, Inverse = true)]
-        public IList<FileTag> Tags
-        {
-            get { return _tags; }
-            set { _tags = value; }
-        }
-
-        [Property]
-        public DateTime Modified { get; set; }
-
-
-        public FileSystemInfo GetFileSystemInfo()
-        {
-            if (String.IsNullOrWhiteSpace(FilePath))
-                throw new InvalidOperationException();
-            Contract.EndContractBlock();
-
-            return Directory.Exists(FilePath) ?
-                (FileSystemInfo)new DirectoryInfo(FilePath)
-                : new FileInfo(FilePath);
-        }
-
-        public string GetRelativeUrl(AccountInfo accountInfo)
-        {
-            if (accountInfo==null)
-                throw new ArgumentNullException("accountInfo");
-            Contract.EndContractBlock();
-
-            var fsi=GetFileSystemInfo();
-            return fsi.AsRelativeUrlTo(accountInfo.AccountPath);
-        }
-        /*public static FileState FindByFilePath(string absolutePath)
-        {
-            if (string.IsNullOrWhiteSpace(absolutePath))
-                throw new ArgumentNullException("absolutePath");
-            Contract.EndContractBlock();
-            try
-            {
-
-                
-
-                return Queryable.FirstOrDefault(s => s.FilePath == absolutePath);
-            }
-            catch (Exception ex)
-            {
-                Log.Error(ex.ToString());
-                throw;
-            }
-
-
-        }*/
-
-       /* public static void DeleteByFilePath(string absolutePath)
-        {
-            if (string.IsNullOrWhiteSpace(absolutePath))
-                throw new ArgumentNullException("absolutePath");
-            Contract.EndContractBlock();
-
-            ExecuteWithRetry((session, instance) =>
-                        {
-                            const string hqlDelete = "delete FileState where FilePath = :path";
-                            var deletedEntities = session.CreateQuery(hqlDelete)
-                                .SetString("path", absolutePath)
-                                .ExecuteUpdate();
-                            return deletedEntities;
-                        }, null);
-
-        }*/
-
-        public static void StoreFileStatus(string absolutePath, FileStatus newStatus)
-        {
-            if (string.IsNullOrWhiteSpace(absolutePath))
-                throw new ArgumentNullException("absolutePath");
-            Contract.EndContractBlock();
-
-            ExecuteWithRetry((session, instance) =>
-                        {
-                            const string hqlUpdate = "update FileState set FileStatus= :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(),
-                                                       FileStatus = newStatus,
-                                                       IsFolder=Directory.Exists(absolutePath)
-                                                   };
-                                newState.CreateAndFlush();
-                            }
-                            return null;
-                        }, null);
-
-        }
-
-        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,
-                                                       IsFolder=Directory.Exists(absolutePath)
-                                                   };
-                                newState.CreateAndFlush();
-                            }
-                            return null;
-                        }, null);
-
-        }
-
-/*
-        public static void UpdateStatus(string absolutePath, FileStatus fileStatus, FileOverlayStatus overlayStatus)
-        {
-            if (string.IsNullOrWhiteSpace(absolutePath))
-                throw new ArgumentNullException("absolutePath");
-            Contract.EndContractBlock();
-
-            ExecuteWithRetry((session, instance) =>
-                        {
-                            const string hqlUpdate =
-                                "update FileState set OverlayStatus= :overlayStatus, FileStatus= :fileStatus where FilePath = :path  ";
-                            var updatedEntities = session.CreateQuery(hqlUpdate)
-                                .SetString("path", absolutePath)
-                                .SetEnum("fileStatus", fileStatus)
-                                .SetEnum("overlayStatus", overlayStatus)
-                                .ExecuteUpdate();
-                            return updatedEntities;
-                        }, null);
-
-        }
-*/
-
-/*
-        public static void UpdateStatus(string absolutePath, FileStatus fileStatus)
-        {
-            if (string.IsNullOrWhiteSpace(absolutePath))
-                throw new ArgumentNullException("absolutePath");
-            Contract.EndContractBlock();
-
-            ExecuteWithRetry((session, instance) =>
-                        {
-                            const string hqlUpdate =
-                                "update FileState set FileStatus= :fileStatus where FilePath = :path  ";
-                            var updatedEntities = session.CreateQuery(hqlUpdate)
-                                .SetString("path", absolutePath)
-                                .SetEnum("fileStatus", fileStatus)
-                                .ExecuteUpdate();
-                            return updatedEntities;
-                        }, null);
-
-        }
-
-*/
-        public static void RenameState(string oldPath, string newPath)
-        {
-            if (string.IsNullOrWhiteSpace(oldPath))
-                throw new ArgumentNullException("oldPath");
-            Contract.EndContractBlock();
-
-            ExecuteWithRetry((session, instance) =>
-                        {
-                            const string hqlUpdate =
-                                "update FileState set FilePath= :newPath where FilePath = :oldPath ";
-                            var updatedEntities = session.CreateQuery(hqlUpdate)
-                                .SetString("oldPath", oldPath)
-                                .SetString("newPath", newPath)
-                                .ExecuteUpdate();
-                            return updatedEntities;
-                        }, null);
-
-        }
-
-     /*   public static void UpdateStatus(Guid id, FileStatus fileStatus)
-        {
-
-            ExecuteWithRetry((session, instance) =>
-            {
-                const string hqlUpdate =
-                    "update FileState set FileStatus= :fileStatus where Id = :id  ";
-                var updatedEntities = session.CreateQuery(hqlUpdate)
-                    .SetGuid("id", id)
-                    .SetEnum("fileStatus", fileStatus)
-                    .ExecuteUpdate();
-                return updatedEntities;
-            }, null);
-        }*/
-
-        public static void UpdateChecksum(string absolutePath, string checksum)
-        {
-            if (string.IsNullOrWhiteSpace(absolutePath))
-                throw new ArgumentNullException("absolutePath");
-            Contract.EndContractBlock();
-
-            ExecuteWithRetry((session, instance) =>
-                        {
-                            const string hqlUpdate = "update FileState set Checksum= :checksum where FilePath = :path ";
-                            var updatedEntities = session.CreateQuery(hqlUpdate)
-                                .SetString("path", absolutePath)
-                                .SetString("checksum", checksum)
-                                .ExecuteUpdate();
-                            return updatedEntities;
-                        }, null);
-
-        }
-
-        public static void ChangeRootPath(string oldPath, string newPath)
-        {
-            if (String.IsNullOrWhiteSpace(oldPath))
-                throw new ArgumentNullException("oldPath");
-            if (!Path.IsPathRooted(oldPath))
-                throw new ArgumentException("oldPath must be an absolute path", "oldPath");
-            if (string.IsNullOrWhiteSpace(newPath))
-                throw new ArgumentNullException("newPath");
-            if (!Path.IsPathRooted(newPath))
-                throw new ArgumentException("newPath must be an absolute path", "newPath");
-            Contract.EndContractBlock();
-
-            //Ensure the paths end with the same character
-            if (!oldPath.EndsWith("\\"))
-                oldPath = oldPath + "\\";
-            if (!newPath.EndsWith("\\"))
-                newPath = newPath + "\\";
-
-                ExecuteWithRetry((session, instance) =>
-                            {
-                                const string hqlUpdate =
-                                    "update FileState set FilePath = replace(FilePath,:oldPath,:newPath) where FilePath like :oldPath || '%' ";
-                                var renames = session.CreateQuery(hqlUpdate)
-                                    .SetString("oldPath", oldPath)
-                                    .SetString("newPath", newPath)
-                                    .ExecuteUpdate();
-                                return renames;
-                            }, null);
-        }
-
-        public static Task<FileState> CreateForAsync(string filePath, int blockSize, string algorithm)
-        {
-            if (blockSize <= 0)
-                throw new ArgumentOutOfRangeException("blockSize");
-            if (String.IsNullOrWhiteSpace(algorithm))
-                throw new ArgumentNullException("algorithm");
-            Contract.EndContractBlock();
-
-
-            var fileState = new FileState
-                                {
-                                    FilePath = filePath,
-                                    OverlayStatus = FileOverlayStatus.Unversioned,
-                                    FileStatus = FileStatus.Created,
-                                    Id = Guid.NewGuid()
-                                };
-
-
-            return fileState.UpdateHashesAsync(blockSize, algorithm);
-        }
-
-        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)
-        {
-            int retries = 3;
-            while (retries > 0)
-                try
-                {
-                    using (new SessionScope())
-                    {
-                        Execute(call, state);
-                        return;
-                    }
-                }
-                catch (ActiveRecordException )
-                {
-                    retries--;
-                    if (retries <= 0)
-                        throw;
-                }
-        }
-
-        /// <summary>
-        /// Remove all FileState rows from the database whose path
-        /// starts with one of the removed paths
-        /// </summary>
-        /// <param name="removed"></param>
-        public static void RemovePaths(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);
-            //Adn retrieveonly the IDs
-            var idQuery = new ProjectionQuery<FileState,Guid>(query.DetachedCriteria,
-                Projections.ProjectionList()
-                .Add(Projections.Id()));
-            
-            var ids=idQuery.Execute();
-            DeleteAll(ids);
-                
-        }
-    }
-
-    [ActiveRecord("Tags")]
-    public class FileTag : ActiveRecordLinqBase<FileTag>
-    {
-        [PrimaryKey]
-        public int Id { get; set; }
-
-        [Property]
-        public string Name { get; set; }
-
-        [Property]
-        public string Value { get; set; }
-
-        [BelongsTo("FileStateId")]
-        public FileState FileState { get; set; }
-
-    }
-   
-}
+#region\r
+/* -----------------------------------------------------------------------\r
+ * <copyright file="FileState.cs" company="GRNet">\r
+ * \r
+ * Copyright 2011-2012 GRNET S.A. All rights reserved.\r
+ *\r
+ * Redistribution and use in source and binary forms, with or\r
+ * without modification, are permitted provided that the following\r
+ * conditions are met:\r
+ *\r
+ *   1. Redistributions of source code must retain the above\r
+ *      copyright notice, this list of conditions and the following\r
+ *      disclaimer.\r
+ *\r
+ *   2. Redistributions in binary form must reproduce the above\r
+ *      copyright notice, this list of conditions and the following\r
+ *      disclaimer in the documentation and/or other materials\r
+ *      provided with the distribution.\r
+ *\r
+ *\r
+ * THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS\r
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\r
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\r
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR\r
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\r
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\r
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF\r
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED\r
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\r
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN\r
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\r
+ * POSSIBILITY OF SUCH DAMAGE.\r
+ *\r
+ * The views and conclusions contained in the software and\r
+ * documentation are those of the authors and should not be\r
+ * interpreted as representing official policies, either expressed\r
+ * or implied, of GRNET S.A.\r
+ * </copyright>\r
+ * -----------------------------------------------------------------------\r
+ */\r
+#endregion\r
+using System.Diagnostics.Contracts;\r
+using System.IO;\r
+using System.Reflection;\r
+using System.Threading.Tasks;\r
+using Castle.ActiveRecord;\r
+using Castle.ActiveRecord.Framework;\r
+using Castle.ActiveRecord.Queries;\r
+using NHibernate.Criterion;\r
+using Pithos.Core.Agents;\r
+using Pithos.Interfaces;\r
+using Pithos.Network;\r
+using log4net;\r
+\r
+namespace Pithos.Core\r
+{\r
+    using System;\r
+    using System.Collections.Generic;    \r
+\r
+    /// <summary>\r
+    /// TODO: Update summary.\r
+    /// </summary>\r
+    [ActiveRecord]\r
+    public class FileState : ActiveRecordLinqBase<FileState>\r
+    {\r
+        private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);\r
+\r
+\r
+        private IList<FileTag> _tags = new List<FileTag>();\r
+\r
+        [PrimaryKey(PrimaryKeyType.Guid)]\r
+        public Guid Id { get; set; }\r
+\r
+        \r
+        //[Property(Unique = true, UniqueKey = "IX_FileState_ObjectID")]\r
+        [Property]\r
+        public string ObjectID { get; set; }\r
+\r
+        [Property(Unique = true, UniqueKey = "IX_FileState_FilePath")]\r
+        public string FilePath { get; set; }\r
+\r
+        [Property]\r
+        public FileOverlayStatus OverlayStatus { get; set; }\r
+\r
+        [Property]\r
+        public FileStatus FileStatus { get; set; }\r
+\r
+        [Property]\r
+        public string ConflictReason { get; set; }\r
+\r
+        private string _checksum;\r
+\r
+        /// <summary>\r
+        /// The tophash value of the file, calculated by using a Merkle hash with the SHA256 algorithm\r
+        /// </summary>\r
+        /// <remarks>\r
+        /// The SHA256 algorithm is substantially more expenive than other algorithms.\r
+        /// Recalculating the Checksum should be avoided whenever possible.\r
+        /// </remarks>\r
+        [Property]\r
+        public string Checksum\r
+        {\r
+            get\r
+            {\r
+                return _checksum;\r
+            }\r
+            set\r
+            {\r
+                _checksum = value;\r
+            }\r
+        }\r
+        \r
+        /// <summary>\r
+        /// An easy to calcualte hash over the entire file, used to detect file changes\r
+        /// </summary>\r
+        /// <remarks>The algorithm used to calculate this hash should be cheap</remarks>\r
+        [Property(NotNull = true, Default = "")]\r
+        public string ETag { get; set; }\r
+\r
+        [Property(NotNull = true, Default = "")]\r
+        public string LastMD5 { get; set; }\r
+\r
+        [Property]\r
+        public string Hashes { get; set; }\r
+\r
+        [Property]\r
+        public DateTime? LastWriteDate { get; set; }\r
+\r
+        [Property]\r
+        public long? LastLength { get; set; }\r
+        \r
+        [Property]\r
+        public long? Version { get; set; }\r
+\r
+        [Property]\r
+        public DateTime? VersionTimeStamp { get; set; }\r
+\r
+\r
+        [Property]\r
+        public bool IsShared { get; set; }\r
+\r
+        [Property]\r
+        public string SharedBy { get; set; }\r
+\r
+        [Property]\r
+        public bool ShareWrite { get; set; }\r
+\r
+        [Property]\r
+        public bool IsFolder{ get; set; }\r
+\r
+        [HasMany(Cascade = ManyRelationCascadeEnum.AllDeleteOrphan, Lazy = true, Inverse = true)]\r
+        public IList<FileTag> Tags\r
+        {\r
+            get { return _tags; }\r
+            set { _tags = value; }\r
+        }\r
+\r
+        [Property]\r
+        public DateTime Modified { get; set; }\r
+\r
+\r
+        public FileSystemInfo GetFileSystemInfo()\r
+        {\r
+            if (String.IsNullOrWhiteSpace(FilePath))\r
+                throw new InvalidOperationException();\r
+            Contract.EndContractBlock();\r
+\r
+            return Directory.Exists(FilePath) ?\r
+                (FileSystemInfo)new DirectoryInfo(FilePath)\r
+                : new FileInfo(FilePath);\r
+        }\r
+\r
+        public string GetRelativeUrl(AccountInfo accountInfo)\r
+        {\r
+            if (accountInfo==null)\r
+                throw new ArgumentNullException("accountInfo");\r
+            Contract.EndContractBlock();\r
+\r
+            var fsi=GetFileSystemInfo();\r
+            return fsi.AsRelativeUrlTo(accountInfo.AccountPath);\r
+        }\r
+        /*public static FileState FindByFilePath(string absolutePath)\r
+        {\r
+            if (string.IsNullOrWhiteSpace(absolutePath))\r
+                throw new ArgumentNullException("absolutePath");\r
+            Contract.EndContractBlock();\r
+            try\r
+            {\r
+\r
+                \r
+\r
+                return Queryable.FirstOrDefault(s => s.FilePath == absolutePath);\r
+            }\r
+            catch (Exception ex)\r
+            {\r
+                Log.Error(ex.ToString());\r
+                throw;\r
+            }\r
+\r
+\r
+        }*/\r
+\r
+       /* public static void DeleteByFilePath(string absolutePath)\r
+        {\r
+            if (string.IsNullOrWhiteSpace(absolutePath))\r
+                throw new ArgumentNullException("absolutePath");\r
+            Contract.EndContractBlock();\r
+\r
+            ExecuteWithRetry((session, instance) =>\r
+                        {\r
+                            const string hqlDelete = "delete FileState where FilePath = :path";\r
+                            var deletedEntities = session.CreateQuery(hqlDelete)\r
+                                .SetString("path", absolutePath)\r
+                                .ExecuteUpdate();\r
+                            return deletedEntities;\r
+                        }, null);\r
+\r
+        }*/\r
+\r
+/*\r
+        public static void StoreFileStatus(string absolutePath, FileStatus newStatus)\r
+        {\r
+            if (string.IsNullOrWhiteSpace(absolutePath))\r
+                throw new ArgumentNullException("absolutePath");\r
+            Contract.EndContractBlock();\r
+\r
+            ExecuteWithRetry((session, instance) =>\r
+                                 {\r
+                                     const string hqlUpdate =\r
+                                         "update FileState set FileStatus= :status where FilePath = :path ";\r
+                                     using (session.BeginTransaction())\r
+                                     {\r
+                                         var updatedEntities = session.CreateQuery(hqlUpdate)\r
+                                             .SetString("path", absolutePath)\r
+                                             .SetEnum("status", newStatus)\r
+                                             .ExecuteUpdate();\r
+                                         if (updatedEntities == 0)\r
+                                         {\r
+                                             var newState = new FileState\r
+                                                                {\r
+                                                                    FilePath = absolutePath,\r
+                                                                    Id = Guid.NewGuid(),\r
+                                                                    FileStatus = newStatus,\r
+                                                                    IsFolder = Directory.Exists(absolutePath)\r
+                                                                };\r
+                                             newState.CreateAndFlush();\r
+                                         }\r
+                                         return null;\r
+                                     }\r
+                                 }, null);\r
+\r
+        }\r
+*/\r
+\r
+        public static void StoreOverlayStatus(string absolutePath, FileOverlayStatus newStatus,string etag)\r
+        {\r
+            if (string.IsNullOrWhiteSpace(absolutePath))\r
+                throw new ArgumentNullException("absolutePath");\r
+            Contract.EndContractBlock();\r
+\r
+            ExecuteWithRetry((session, instance) =>\r
+                        {\r
+                            const string hqlUpdate =\r
+                                "update FileState set OverlayStatus= :status where FilePath = :path ";\r
+                            using (var tx=session.BeginTransaction())\r
+                            {\r
+                                var updatedEntities = session.CreateQuery(hqlUpdate)\r
+                                    .SetString("path", absolutePath)\r
+                                    .SetEnum("status", newStatus)\r
+                                    .ExecuteUpdate();\r
+                                if (updatedEntities == 0)\r
+                                {\r
+                                    var newState = new FileState\r
+                                                       {\r
+                                                           FilePath = absolutePath,\r
+                                                           Id = Guid.NewGuid(),\r
+                                                           OverlayStatus = newStatus,\r
+                                                           ETag = etag ?? String.Empty,\r
+                                                           LastMD5=String.Empty,\r
+                                                           IsFolder = Directory.Exists(absolutePath)\r
+                                                       };\r
+                                    newState.CreateAndFlush();\r
+                                }\r
+                                tx.Commit();\r
+                                return null;\r
+                            }\r
+                        }, null);\r
+\r
+        }\r
+\r
+/*\r
+        public static void UpdateStatus(string absolutePath, FileStatus fileStatus, FileOverlayStatus overlayStatus)\r
+        {\r
+            if (string.IsNullOrWhiteSpace(absolutePath))\r
+                throw new ArgumentNullException("absolutePath");\r
+            Contract.EndContractBlock();\r
+\r
+            ExecuteWithRetry((session, instance) =>\r
+                        {\r
+                            const string hqlUpdate =\r
+                                "update FileState set OverlayStatus= :overlayStatus, FileStatus= :fileStatus where FilePath = :path  ";\r
+                            var updatedEntities = session.CreateQuery(hqlUpdate)\r
+                                .SetString("path", absolutePath)\r
+                                .SetEnum("fileStatus", fileStatus)\r
+                                .SetEnum("overlayStatus", overlayStatus)\r
+                                .ExecuteUpdate();\r
+                            return updatedEntities;\r
+                        }, null);\r
+\r
+        }\r
+*/\r
+\r
+/*\r
+        public static void UpdateStatus(string absolutePath, FileStatus fileStatus)\r
+        {\r
+            if (string.IsNullOrWhiteSpace(absolutePath))\r
+                throw new ArgumentNullException("absolutePath");\r
+            Contract.EndContractBlock();\r
+\r
+            ExecuteWithRetry((session, instance) =>\r
+                        {\r
+                            const string hqlUpdate =\r
+                                "update FileState set FileStatus= :fileStatus where FilePath = :path  ";\r
+                            var updatedEntities = session.CreateQuery(hqlUpdate)\r
+                                .SetString("path", absolutePath)\r
+                                .SetEnum("fileStatus", fileStatus)\r
+                                .ExecuteUpdate();\r
+                            return updatedEntities;\r
+                        }, null);\r
+\r
+        }\r
+\r
+*/\r
+        public static void RenameState(string oldPath, string newPath)\r
+        {\r
+            if (string.IsNullOrWhiteSpace(oldPath))\r
+                throw new ArgumentNullException("oldPath");\r
+            Contract.EndContractBlock();\r
+\r
+            ExecuteWithRetry((session, instance) =>\r
+                        {\r
+                            const string hqlUpdate =\r
+                                "update FileState set FilePath= :newPath where FilePath = :oldPath ";\r
+                            var updatedEntities = session.CreateQuery(hqlUpdate)\r
+                                .SetString("oldPath", oldPath)\r
+                                .SetString("newPath", newPath)\r
+                                .ExecuteUpdate();\r
+                            return updatedEntities;\r
+                        }, null);\r
+\r
+        }\r
+\r
+     /*   public static void UpdateStatus(Guid id, FileStatus fileStatus)\r
+        {\r
+\r
+            ExecuteWithRetry((session, instance) =>\r
+            {\r
+                const string hqlUpdate =\r
+                    "update FileState set FileStatus= :fileStatus where Id = :id  ";\r
+                var updatedEntities = session.CreateQuery(hqlUpdate)\r
+                    .SetGuid("id", id)\r
+                    .SetEnum("fileStatus", fileStatus)\r
+                    .ExecuteUpdate();\r
+                return updatedEntities;\r
+            }, null);\r
+        }*/\r
+\r
+        public static void UpdateFileTreeHash(string absolutePath, TreeHash treeHash)\r
+        {\r
+            if (string.IsNullOrWhiteSpace(absolutePath))\r
+                throw new ArgumentNullException("absolutePath");\r
+            Contract.EndContractBlock();\r
+\r
+            ExecuteWithRetry((session, instance) =>\r
+            {\r
+                \r
+                var hashes=treeHash.ToJson();\r
+                const string hqlUpdate = "update FileState set Checksum= :checksum,ETag=:etag, Hashes=:hashes where FilePath = :path ";\r
+                var updatedEntities = session.CreateQuery(hqlUpdate)\r
+                    .SetString("path", absolutePath)\r
+                    .SetString("checksum", treeHash.TopHash.ToHashString())\r
+                    .SetString("etag", treeHash.MD5)\r
+                    .SetString("hashes",hashes)\r
+                    .ExecuteUpdate();\r
+                return updatedEntities;\r
+            }, null);\r
+        }\r
+\r
+        public static void UpdateChecksum(string absolutePath, string etag, string checksum)\r
+        {\r
+            if (string.IsNullOrWhiteSpace(absolutePath))\r
+                throw new ArgumentNullException("absolutePath");\r
+            Contract.EndContractBlock();\r
+\r
+            ExecuteWithRetry((session, instance) =>\r
+                        {\r
+                            const string hqlUpdate = "update FileState set Checksum= :checksum,ETag=:etag where FilePath = :path ";\r
+                            var updatedEntities = session.CreateQuery(hqlUpdate)\r
+                                .SetString("path", absolutePath)\r
+                                .SetString("checksum", checksum)\r
+                                .SetString("etag", etag)\r
+                                .ExecuteUpdate();\r
+                            return updatedEntities;\r
+                        }, null);\r
+\r
+        }\r
+\r
+\r
+        public static void UpdateLastMD5(FileInfo file, string md5)\r
+        {\r
+            if (file==null)\r
+                throw new ArgumentNullException("file");\r
+            Contract.EndContractBlock();\r
+\r
+            ExecuteWithRetry((session, instance) =>\r
+                        {\r
+                            const string hqlUpdate = "update FileState set LastMD5=:md5, LastWriteDate=:date,LastLength=:length where FilePath = :path ";\r
+                            \r
+                            file.Refresh();\r
+                            if (!file.Exists)\r
+                                return 0;\r
+                            var fullName = file.WithProperCapitalization().FullName;\r
+\r
+                            using (var tx=session.BeginTransaction())\r
+                            {\r
+                                var updatedEntities = session.CreateQuery(hqlUpdate)\r
+                                    .SetDateTime("date", file.LastWriteTime)\r
+                                    .SetInt64("length", file.Length)\r
+                                    .SetString("md5", md5)\r
+                                    .SetString("path", fullName)\r
+                                    .ExecuteUpdate();\r
+                                if (updatedEntities == 0)\r
+                                {\r
+                                    var newState = new FileState\r
+                                                       {\r
+                                                           FilePath = fullName,\r
+                                                           Id = Guid.NewGuid(),\r
+                                                           OverlayStatus = FileOverlayStatus.Normal,\r
+                                                           FileStatus = FileStatus.Unchanged,\r
+                                                           IsFolder = false,\r
+                                                           LastLength = file.Length,\r
+                                                           LastWriteDate = file.LastWriteTime,\r
+                                                           LastMD5 = md5 ?? String.Empty,\r
+                                                           ETag = String.Empty\r
+                                                       };\r
+                                    newState.CreateAndFlush();\r
+                                }\r
+                                tx.Commit();\r
+                                return updatedEntities;\r
+                            }\r
+                        }, null);\r
+\r
+        }\r
+\r
+        public static void ChangeRootPath(string oldPath, string newPath)\r
+        {\r
+            if (String.IsNullOrWhiteSpace(oldPath))\r
+                throw new ArgumentNullException("oldPath");\r
+            if (!Path.IsPathRooted(oldPath))\r
+                throw new ArgumentException("oldPath must be an absolute path", "oldPath");\r
+            if (string.IsNullOrWhiteSpace(newPath))\r
+                throw new ArgumentNullException("newPath");\r
+            if (!Path.IsPathRooted(newPath))\r
+                throw new ArgumentException("newPath must be an absolute path", "newPath");\r
+            Contract.EndContractBlock();\r
+\r
+            //Ensure the paths end with the same character\r
+            if (!oldPath.EndsWith("\\"))\r
+                oldPath = oldPath + "\\";\r
+            if (!newPath.EndsWith("\\"))\r
+                newPath = newPath + "\\";\r
+\r
+                ExecuteWithRetry((session, instance) =>\r
+                            {\r
+                                const string hqlUpdate =\r
+                                    "update FileState set FilePath = replace(FilePath,:oldPath,:newPath) where FilePath like :oldPath || '%' ";\r
+                                var renames = session.CreateQuery(hqlUpdate)\r
+                                    .SetString("oldPath", oldPath)\r
+                                    .SetString("newPath", newPath)\r
+                                    .ExecuteUpdate();\r
+                                return renames;\r
+                            }, null);\r
+        }\r
+\r
+        public static FileState CreateFor(FileSystemInfo info,IStatusNotification notification)\r
+        {\r
+            if(info==null)\r
+                throw new ArgumentNullException("info");\r
+            Contract.EndContractBlock();\r
+            \r
+            if (info is DirectoryInfo)\r
+                return new FileState\r
+                {\r
+                    FilePath = info.FullName,\r
+                    OverlayStatus = FileOverlayStatus.Unversioned,\r
+                    FileStatus = FileStatus.Created,\r
+                    ETag=String.Empty,\r
+                    LastMD5=String.Empty,\r
+                    Id = Guid.NewGuid()\r
+                };\r
+\r
+            \r
+            var etag = ((FileInfo)info).ComputeShortHash(notification);\r
+            var fileState = new FileState\r
+                                {\r
+                                    FilePath = info.FullName,\r
+                                    OverlayStatus = FileOverlayStatus.Unversioned,\r
+                                    FileStatus = FileStatus.Created,               \r
+                                    ETag=etag,\r
+                                    LastMD5=String.Empty,\r
+                                    Id = Guid.NewGuid()\r
+                                };\r
+            return fileState;\r
+        }\r
+\r
+\r
+        private static void ExecuteWithRetry(NHibernateDelegate call, object state)\r
+        {\r
+            int retries = 3;\r
+            while (retries > 0)\r
+                try\r
+                {\r
+                    using (new SessionScope())\r
+                    {\r
+                        Execute(call, state);\r
+                        return;\r
+                    }\r
+                }\r
+                catch (ActiveRecordException )\r
+                {\r
+                    retries--;\r
+                    if (retries <= 0)\r
+                        throw;\r
+                }\r
+        }\r
+\r
+        /// <summary>\r
+        /// Mark Unversioned all FileState rows from the database whose path\r
+        /// starts with one of the removed paths\r
+        /// </summary>\r
+        /// <param name="removed"></param>\r
+        public static void UnversionPaths(List<string> removed)\r
+        {\r
+            if (removed == null)\r
+                return;\r
+            if (removed.Count == 0)\r
+                return;\r
+\r
+            //Create a disjunction (list of OR statements\r
+            var disjunction = new Disjunction();            \r
+            foreach (var path in removed)\r
+            {\r
+                //with the restriction FileState.FilePath like '@path%'\r
+                disjunction.Add(Restrictions.On<FileState>(s => s.FilePath)\r
+                    .IsLike(path, MatchMode.Start));\r
+            }\r
+\r
+            //Generate a query from the disjunction\r
+            var query=QueryOver.Of<FileState>().Where(disjunction);\r
+                        \r
+            ExecuteWithRetry((session,instance)=>\r
+                                 {\r
+                                     using (var tx=session.BeginTransaction())\r
+                                     {\r
+                                         var states = query.GetExecutableQueryOver(session).List();\r
+                                         foreach (var state in states)\r
+                                         {\r
+                                             state.FileStatus = FileStatus.Unversioned;\r
+                                             state.OverlayStatus = FileOverlayStatus.Unversioned;\r
+                                             state.Update();\r
+                                         }\r
+                                         tx.Commit();\r
+                                     }\r
+                                     return null;\r
+                                 },null);\r
+        }\r
+\r
+        public string ToDebugString()\r
+        {\r
+            return String.Format("[STATE] FilePath:[{0}] ObjectID:[{1}], ETAG: [{2}], LastMD5:[{3}]", FilePath, ObjectID,\r
+                                 ETag, LastMD5);\r
+        }\r
+    }\r
+\r
+    [ActiveRecord("Tags")]\r
+    public class FileTag : ActiveRecordLinqBase<FileTag>\r
+    {\r
+        [PrimaryKey]\r
+        public int Id { get; set; }\r
+\r
+        [Property]\r
+        public string Name { get; set; }\r
+\r
+        [Property]\r
+        public string Value { get; set; }\r
+\r
+        [BelongsTo("FileStateId")]\r
+        public FileState FileState { get; set; }\r
+\r
+    }\r
+   \r
+}\r