Revision 174bbb6e trunk/Pithos.Core/Agents/StatusAgent.cs

b/trunk/Pithos.Core/Agents/StatusAgent.cs
48 48
using System.IO;
49 49
using System.Linq;
50 50
using System.Reflection;
51
using System.Security.Cryptography;
51 52
using System.Text;
52 53
using System.Threading;
53 54
using System.Threading.Tasks;
54 55
using Castle.ActiveRecord;
56
using Castle.ActiveRecord.Framework;
55 57
using Castle.ActiveRecord.Framework.Config;
56 58
using Pithos.Interfaces;
57 59
using Pithos.Network;
......
185 187
                        {
186 188
                            Log.ErrorFormat("[ERROR] STATE \n{0}", ex);
187 189
                        }
190
                        queue.NotifyComplete(action);
188 191
// ReSharper disable AccessToModifiedClosure
189 192
                        queue.DoAsync(loop);
190 193
// ReSharper restore AccessToModifiedClosure
......
201 204
        {
202 205
            _persistenceAgent.Stop();            
203 206
        }
204
       
207
               
205 208

  
206 209
        public void ProcessExistingFiles(IEnumerable<FileInfo> existingFiles)
207 210
        {
......
230 233
                        where missingStates.Contains(state.Id)
231 234
                        select new { File = default(FileInfo), State = state };
232 235

  
233
            var pairs = currentFiles.Union(deletedFiles);
236
            var pairs = currentFiles.Union(deletedFiles).ToList();
234 237

  
235
            foreach(var pair in pairs)
238
            using (var shortHasher = HashAlgorithm.Create("sha1"))
236 239
            {
237
                var fileState = pair.State;
238
                var file = pair.File;
239
                if (fileState == null)
240
                {
241
                    //This is a new file
242
                    var fullPath = pair.File.FullName;
243
                    var createState = FileState.CreateForAsync(fullPath, BlockSize, BlockHash);
244
                    createState.ContinueWith(state => _persistenceAgent.Post(state.Result.Create));
245
                }                
246
                else if (file == null)
240
                foreach (var pair in pairs)
247 241
                {
248
                    //This file was deleted while we were down. We should mark it as deleted
249
                    //We have to go through UpdateStatus here because the state object we are using
250
                    //was created by a different ORM session.
251
                    _persistenceAgent.Post(()=> UpdateStatusDirect(fileState.Id, FileStatus.Deleted));                    
252
                }
253
                else
254
                {
255
                    //This file has a matching state. Need to check for possible changes
256
                    var hashString = file.CalculateHash(BlockSize,BlockHash);
257
                    //TODO: Need a way to attach the hashes to the filestate so we don't
258
                    //recalculate them each time a call to calculate has is made
259
                    //We can either store them to the filestate or add them to a 
260
                    //dictionary
261

  
262
                    //If the hashes don't match the file was changed
263
                    if (fileState.Checksum != hashString)
242
                    var fileState = pair.State;
243
                    var file = pair.File;
244
                    if (fileState == null)
264 245
                    {
265
                        _persistenceAgent.Post(() => UpdateStatusDirect(fileState.Id, FileStatus.Modified));
266
                    }                    
246
                        //This is a new file                        
247
                        var createState = FileState.CreateFor(file);
248
                        _persistenceAgent.Post(createState.Create);                        
249
                    }
250
                    else if (file == null)
251
                    {
252
                        //This file was deleted while we were down. We should mark it as deleted
253
                        //We have to go through UpdateStatus here because the state object we are using
254
                        //was created by a different ORM session.
255
                        _persistenceAgent.Post(() => UpdateStatusDirect(fileState.Id, FileStatus.Deleted));
256
                    }
257
                    else
258
                    {
259
                        //This file has a matching state. Need to check for possible changes
260
                        //To check for changes, we use the cheap (in CPU terms) SHA1 algorithm
261
                        //on the entire file.
262
                        
263
                        var hashString = file.ComputeShortHash(shortHasher);                        
264
                        //TODO: Need a way to attach the hashes to the filestate so we don't
265
                        //recalculate them each time a call to calculate has is made
266
                        //We can either store them to the filestate or add them to a 
267
                        //dictionary
268

  
269
                        //If the hashes don't match the file was changed
270
                        if (fileState.ShortHash != hashString)
271
                        {
272
                            _persistenceAgent.Post(() => UpdateStatusDirect(fileState.Id, FileStatus.Modified));
273
                        }
274
                    }
267 275
                }
268
            };            
276
            }
277
                        
269 278
         
270 279
        }
280
        
281

  
271 282

  
272 283
        private int UpdateStatusDirect(Guid id, FileStatus status)
273 284
        {
......
387 398

  
388 399
        }
389 400

  
390
        private PithosStatus _pithosStatus=PithosStatus.InSynch;       
391

  
392
        public void SetPithosStatus(PithosStatus status)
393
        {
394
            _pithosStatus = status;
395
        }
396

  
397
        public PithosStatus GetPithosStatus()
398
        {
399
            return _pithosStatus;
400
        }
401 401

  
402 402

  
403 403
        private readonly string _pithosDataPath;
......
415 415
            {
416 416
                
417 417
                using (var connection = GetConnection())
418
                using (var command = new SQLiteCommand("select Id, FilePath, OverlayStatus,FileStatus ,Checksum   ,Version    ,VersionTimeStamp,IsShared   ,SharedBy   ,ShareWrite  from FileState where FilePath=:path COLLATE NOCASE", connection))
418
                using (var command = new SQLiteCommand("select Id, FilePath, OverlayStatus,FileStatus ,Checksum ,ShortHash,Version    ,VersionTimeStamp,IsShared   ,SharedBy   ,ShareWrite  from FileState where FilePath=:path COLLATE NOCASE", connection))
419 419
                {
420 420
                    
421 421
                    command.Parameters.AddWithValue("path", path);
......
433 433
                                                OverlayStatus =reader.IsDBNull(2)?FileOverlayStatus.Unversioned: (FileOverlayStatus) reader.GetInt64(2),
434 434
                                                FileStatus = reader.IsDBNull(3)?FileStatus.Missing:(FileStatus) reader.GetInt64(3),
435 435
                                                Checksum = reader.IsDBNull(4)?"":reader.GetString(4),
436
                                                Version = reader.IsDBNull(5)?default(long):reader.GetInt64(5),
437
                                                VersionTimeStamp = reader.IsDBNull(6)?default(DateTime):reader.GetDateTime(6),
438
                                                IsShared = !reader.IsDBNull(7) && reader.GetBoolean(7),
439
                                                SharedBy = reader.IsDBNull(8)?"":reader.GetString(8),
440
                                                ShareWrite = !reader.IsDBNull(9) && reader.GetBoolean(9)
436
                                                ShortHash= reader.IsDBNull(5)?"":reader.GetString(5),
437
                                                Version = reader.IsDBNull(6)?default(long):reader.GetInt64(6),
438
                                                VersionTimeStamp = reader.IsDBNull(7)?default(DateTime):reader.GetDateTime(7),
439
                                                IsShared = !reader.IsDBNull(8) && reader.GetBoolean(8),
440
                                                SharedBy = reader.IsDBNull(9)?"":reader.GetString(9),
441
                                                ShareWrite = !reader.IsDBNull(10) && reader.GetBoolean(10)
441 442
                                            };
442 443
/*
443 444
                            var state = new FileState
......
518 519
            return connection;
519 520
        }
520 521

  
521
        public void SetFileOverlayStatus(string path, FileOverlayStatus overlayStatus)
522
       /* public void SetFileOverlayStatus(string path, FileOverlayStatus overlayStatus)
522 523
        {
523 524
            if (String.IsNullOrWhiteSpace(path))
524 525
                throw new ArgumentNullException("path");
......
527 528
            Contract.EndContractBlock();
528 529

  
529 530
            _persistenceAgent.Post(() => FileState.StoreOverlayStatus(path,overlayStatus));
531
        }*/
532

  
533
        public Task SetFileOverlayStatus(string path, FileOverlayStatus overlayStatus, string shortHash = null)
534
        {
535
            if (String.IsNullOrWhiteSpace(path))
536
                throw new ArgumentNullException("path");
537
            if (!Path.IsPathRooted(path))
538
                throw new ArgumentException("The path must be rooted","path");
539
            Contract.EndContractBlock();
540

  
541
            return _persistenceAgent.PostAndAwait(() => FileState.StoreOverlayStatus(path,overlayStatus,shortHash));
530 542
        }
531 543

  
532 544
       /* public void RenameFileOverlayStatus(string oldPath, string newPath)
......
627 639
                    else
628 640
                    {
629 641
                        command.CommandText =
630
                            "INSERT INTO FileState (Id,FilePath,Checksum,Version,VersionTimeStamp,FileStatus,OverlayStatus) VALUES (:id,:path,:checksum,:version,:versionTimeStamp,:fileStatus,:overlayStatus)";
642
                            "INSERT INTO FileState (Id,FilePath,Checksum,Version,VersionTimeStamp,ShortHash,FileStatus,OverlayStatus) VALUES (:id,:path,:checksum,:version,:versionTimeStamp,:shortHash,:fileStatus,:overlayStatus)";
631 643
                        command.Parameters.AddWithValue("id", Guid.NewGuid());
632 644
                    }
633 645

  
634 646
                    command.Parameters.AddWithValue("path", path);
635 647
                    command.Parameters.AddWithValue("checksum", objectInfo.Hash);
648
                    command.Parameters.AddWithValue("shortHash", "");
636 649
                    command.Parameters.AddWithValue("version", objectInfo.Version);
637 650
                    command.Parameters.AddWithValue("versionTimeStamp",
638 651
                                                    objectInfo.VersionTimestamp);
......
736 749
            return children;
737 750
        }
738 751

  
752
        public void EnsureFileState(string path)
753
        {
754
            var existingState = GetStateByFilePath(path);
755
            if (existingState != null)
756
                return;
757
            var fileInfo = FileInfoExtensions.FromPath(path);
758
            using (new SessionScope())
759
            {
760
                var newState = FileState.CreateFor(fileInfo);
761
                newState.FileStatus=FileStatus.Missing;
762
                _persistenceAgent.PostAndAwait(newState.CreateAndFlush).Wait();
763
            }
764

  
765
        }
766

  
739 767
        private int DeleteDirect(string filePath)
740 768
        {
741 769
            using (log4net.ThreadContext.Stacks["StatusAgent"].Push("DeleteDirect"))
......
792 820
            }
793 821
        }
794 822

  
795
        public void UpdateFileChecksum(string path, string checksum)
823
        public void UpdateFileChecksum(string path, string shortHash, string checksum)
796 824
        {
797 825
            if (String.IsNullOrWhiteSpace(path))
798 826
                throw new ArgumentNullException("path");
......
800 828
                throw new ArgumentException("The path must be rooted", "path");            
801 829
            Contract.EndContractBlock();
802 830

  
803
            _persistenceAgent.Post(() => FileState.UpdateChecksum(path, checksum));
831
            _persistenceAgent.Post(() => FileState.UpdateChecksum(path, shortHash,checksum));
804 832
        }
805 833

  
806 834
    }

Also available in: Unified diff