Statistics
| Branch: | Revision:

root / trunk / Pithos.Core / Agents / StatusAgent.cs @ 540b8cf8

History | View | Annotate | Download (29.1 kB)

1
using System;
2
using System.Collections.Generic;
3
using System.ComponentModel.Composition;
4
using System.Data.SQLite;
5
using System.Diagnostics;
6
using System.Diagnostics.Contracts;
7
using System.IO;
8
using System.Linq;
9
using System.Text;
10
using System.Threading;
11
using System.Threading.Tasks;
12
using Castle.ActiveRecord;
13
using Castle.ActiveRecord.Framework.Config;
14
using Pithos.Interfaces;
15
using Pithos.Network;
16
using log4net;
17

    
18
namespace Pithos.Core.Agents
19
{
20
    [Export(typeof(IStatusChecker)),Export(typeof(IStatusKeeper))]
21
    public class StatusAgent:IStatusChecker,IStatusKeeper
22
    {
23
        [System.ComponentModel.Composition.Import]
24
        public IPithosSettings Settings { get; set; }
25

    
26
        private Agent<Action> _persistenceAgent;
27

    
28

    
29
        private static readonly ILog Log = LogManager.GetLogger("StatusAgent");
30

    
31
        public StatusAgent()
32
        {            
33
            var appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
34
            _pithosDataPath = Path.Combine(appDataPath , "Pithos");
35

    
36
            if (!Directory.Exists(_pithosDataPath))
37
                Directory.CreateDirectory(_pithosDataPath);
38
            var source = GetConfiguration(_pithosDataPath);
39
            ActiveRecordStarter.Initialize(source,typeof(FileState),typeof(FileTag));
40
            ActiveRecordStarter.UpdateSchema();
41

    
42
            if (!File.Exists(Path.Combine(_pithosDataPath ,"pithos.db")))
43
                ActiveRecordStarter.CreateSchema();
44

    
45
            CreateTrigger();
46
        }
47

    
48
        private void CreateTrigger()
49
        {
50
            using (var connection = GetConnection())
51
            using (var triggerCommand = connection.CreateCommand())
52
            {
53
                var cmdText = new StringBuilder()
54
                    .AppendLine("CREATE TRIGGER IF NOT EXISTS update_last_modified UPDATE ON FileState FOR EACH ROW")
55
                    .AppendLine("BEGIN")
56
                    .AppendLine("UPDATE FileState SET Modified=datetime('now')  WHERE Id=old.Id;")
57
                    .AppendLine("END;")
58
                    .AppendLine("CREATE TRIGGER IF NOT EXISTS insert_last_modified INSERT ON FileState FOR EACH ROW")
59
                    .AppendLine("BEGIN")
60
                    .AppendLine("UPDATE FileState SET Modified=datetime('now')  WHERE Id=new.Id;")
61
                    .AppendLine("END;")
62
                    .ToString();
63
                triggerCommand.CommandText = cmdText;                
64
                triggerCommand.ExecuteNonQuery();
65
            }
66
        }
67

    
68

    
69
        private static InPlaceConfigurationSource GetConfiguration(string pithosDbPath)
70
        {
71
            if (String.IsNullOrWhiteSpace(pithosDbPath))
72
                throw new ArgumentNullException("pithosDbPath");
73
            if (!Path.IsPathRooted(pithosDbPath))
74
                throw new ArgumentException("path must be a rooted path", "pithosDbPath");
75
            Contract.EndContractBlock();
76

    
77
            var properties = new Dictionary<string, string>
78
                                 {
79
                                     {"connection.driver_class", "NHibernate.Driver.SQLite20Driver"},
80
                                     {"dialect", "NHibernate.Dialect.SQLiteDialect"},
81
                                     {"connection.provider", "NHibernate.Connection.DriverConnectionProvider"},
82
                                     {
83
                                         "proxyfactory.factory_class",
84
                                         "NHibernate.ByteCode.Castle.ProxyFactoryFactory, NHibernate.ByteCode.Castle"
85
                                         },
86
                                 };
87

    
88
            var connectionString = String.Format(@"Data Source={0}\pithos.db;Version=3;Enlist=N", pithosDbPath);
89
            properties.Add("connection.connection_string", connectionString);
90

    
91
            var source = new InPlaceConfigurationSource();                        
92
            source.Add(typeof (ActiveRecordBase), properties);
93
            source.SetDebugFlag(false);            
94
            return source;
95
        }
96

    
97
        public void StartProcessing(CancellationToken token)
98
        {
99
            _persistenceAgent = Agent<Action>.Start(queue =>
100
            {
101
                Action loop = null;
102
                loop = () =>
103
                {
104
                    var job = queue.Receive();
105
                    job.ContinueWith(t =>
106
                    {
107
                        var action = job.Result;
108
                        try
109
                        {
110
                            action();
111
                        }
112
                        catch (SQLiteException ex)
113
                        {
114
                            Log.ErrorFormat("[ERROR] SQL \n{0}", ex);
115
                        }
116
                        catch (Exception ex)
117
                        {
118
                            Log.ErrorFormat("[ERROR] STATE \n{0}", ex);
119
                        }
120
// ReSharper disable AccessToModifiedClosure
121
                        queue.DoAsync(loop);
122
// ReSharper restore AccessToModifiedClosure
123
                    });
124
                };
125
                loop();
126
            });
127
            
128
        }
129

    
130
       
131

    
132
        public void Stop()
133
        {
134
            _persistenceAgent.Stop();            
135
        }
136
       
137

    
138
        public void ProcessExistingFiles(IEnumerable<FileInfo> existingFiles)
139
        {
140
            if(existingFiles  ==null)
141
                throw new ArgumentNullException("existingFiles");
142
            Contract.EndContractBlock();
143
            
144
            //Find new or matching files with a left join to the stored states
145
            var fileStates = FileState.Queryable;
146
            var currentFiles=from file in existingFiles
147
                      join state in fileStates on file.FullName.ToLower() equals state.FilePath.ToLower() into gs
148
                      from substate in gs.DefaultIfEmpty()
149
                               select new {File = file, State = substate};
150

    
151
            //To get the deleted files we must get the states that have no corresponding
152
            //files. 
153
            //We can't use the File.Exists method inside a query, so we get all file paths from the states
154
            var statePaths = (from state in fileStates
155
                             select new {state.Id, state.FilePath}).ToList();
156
            //and check each one
157
            var missingStates= (from path in statePaths
158
                                where !File.Exists(path.FilePath) && !Directory.Exists(path.FilePath)
159
                               select path.Id).ToList();
160
            //Finally, retrieve the states that correspond to the deleted files            
161
            var deletedFiles = from state in fileStates 
162
                        where missingStates.Contains(state.Id)
163
                        select new { File = default(FileInfo), State = state };
164

    
165
            var pairs = currentFiles.Union(deletedFiles);
166

    
167
            foreach(var pair in pairs)
168
            {
169
                var fileState = pair.State;
170
                var file = pair.File;
171
                if (fileState == null)
172
                {
173
                    //This is a new file
174
                    var fullPath = pair.File.FullName;
175
                    var createState = FileState.CreateForAsync(fullPath, BlockSize, BlockHash);
176
                    createState.ContinueWith(state => _persistenceAgent.Post(state.Result.Create));
177
                }                
178
                else if (file == null)
179
                {
180
                    //This file was deleted while we were down. We should mark it as deleted
181
                    //We have to go through UpdateStatus here because the state object we are using
182
                    //was created by a different ORM session.
183
                    _persistenceAgent.Post(()=> UpdateStatusDirect(fileState.Id, FileStatus.Deleted));                    
184
                }
185
                else
186
                {
187
                    //This file has a matching state. Need to check for possible changes
188
                    var hashString = file.CalculateHash(BlockSize,BlockHash);
189
                    //If the hashes don't match the file was changed
190
                    if (fileState.Checksum != hashString)
191
                    {
192
                        _persistenceAgent.Post(() => UpdateStatusDirect(fileState.Id, FileStatus.Modified));
193
                    }                    
194
                }
195
            };            
196
         
197
        }
198

    
199
        private int UpdateStatusDirect(Guid id, FileStatus status)
200
        {
201
            using (log4net.ThreadContext.Stacks["StatusAgent"].Push("UpdateStatusDirect"))
202
            {
203

    
204
                try
205
                {
206
                    
207
                    using (var connection = GetConnection())
208
                    using (
209
                        var command = new SQLiteCommand("update FileState set FileStatus= :fileStatus where Id = :id  ",
210
                                                        connection))
211
                    {                                                
212
                        command.Parameters.AddWithValue("fileStatus", status);
213

    
214
                        command.Parameters.AddWithValue("id", id);
215
                        
216
                        var affected = command.ExecuteNonQuery();
217
                        
218
                        return affected;
219
                    }
220

    
221
                }
222
                catch (Exception exc)
223
                {
224
                    Log.Error(exc.ToString());
225
                    throw;
226
                }
227
            }
228
        }
229
        
230
        private int UpdateStatusDirect(string path, FileStatus status)
231
        {
232
            using (log4net.ThreadContext.Stacks["StatusAgent"].Push("UpdateStatusDirect"))
233
            {
234

    
235
                try
236
                {
237

    
238
                    
239
                    using (var connection = GetConnection())
240
                    using (
241
                        var command =
242
                            new SQLiteCommand("update FileState set FileStatus= :fileStatus where FilePath = :path COLLATE NOCASE",
243
                                              connection))
244
                    {
245

    
246

    
247
                        command.Parameters.AddWithValue("fileStatus", status);
248

    
249
                        command.Parameters.AddWithValue("path", path);
250
                        
251
                        var affected = command.ExecuteNonQuery();
252
                        return affected;
253
                    }
254
                }
255
                catch (Exception exc)
256
                {
257
                    Log.Error(exc.ToString());
258
                    throw;
259
                }
260
            }
261
        }
262

    
263
        private int UpdateStatusDirect(string absolutePath, FileStatus fileStatus, FileOverlayStatus overlayStatus)
264
        {
265
            using (log4net.ThreadContext.Stacks["StatusAgent"].Push("UpdateStatusDirect"))
266
            {
267

    
268
                try
269
                {
270

    
271
                    
272
                    using (var connection = GetConnection())
273
                    using (
274
                        var command =
275
                            new SQLiteCommand(
276
                                "update FileState set OverlayStatus= :overlayStatus, FileStatus= :fileStatus where FilePath = :path COLLATE NOCASE ",
277
                                connection))
278
                    {
279

    
280
                        command.Parameters.AddWithValue("path", absolutePath);
281
                        command.Parameters.AddWithValue("fileStatus", fileStatus);
282
                        command.Parameters.AddWithValue("overlayStatus", overlayStatus);
283
                        
284
                        var affected = command.ExecuteNonQuery();
285
                        return affected;
286
                    }
287
                }
288
                catch (Exception exc)
289
                {
290
                    Log.Error(exc.ToString());
291
                    throw;
292
                }
293
            }
294
        }
295
        
296

    
297

    
298
        public string BlockHash { get; set; }
299

    
300
        public int BlockSize { get; set; }
301
        public void ChangeRoots(string oldPath, string newPath)
302
        {
303
            if (String.IsNullOrWhiteSpace(oldPath))
304
                throw new ArgumentNullException("oldPath");
305
            if (!Path.IsPathRooted(oldPath))
306
                throw new ArgumentException("oldPath must be an absolute path", "oldPath");
307
            if (string.IsNullOrWhiteSpace(newPath))
308
                throw new ArgumentNullException("newPath");
309
            if (!Path.IsPathRooted(newPath))
310
                throw new ArgumentException("newPath must be an absolute path", "newPath");
311
            Contract.EndContractBlock();
312

    
313
            FileState.ChangeRootPath(oldPath,newPath);
314

    
315
        }
316

    
317
        private PithosStatus _pithosStatus=PithosStatus.InSynch;       
318

    
319
        public void SetPithosStatus(PithosStatus status)
320
        {
321
            _pithosStatus = status;
322
        }
323

    
324
        public PithosStatus GetPithosStatus()
325
        {
326
            return _pithosStatus;
327
        }
328

    
329

    
330
        private readonly string _pithosDataPath;
331

    
332

    
333
        public FileState GetStateByFilePath(string path)
334
        {
335
            if (String.IsNullOrWhiteSpace(path))
336
                throw new ArgumentNullException("path");
337
            if (!Path.IsPathRooted(path))
338
                throw new ArgumentException("The path must be rooted", "path");
339
            Contract.EndContractBlock();
340

    
341
            try
342
            {
343
                
344
                using (var connection = GetConnection())
345
                using (var command = new SQLiteCommand("select Id, FilePath, OverlayStatus,FileStatus ,Checksum   ,Version    ,VersionTimeStamp,IsShared   ,SharedBy   ,ShareWrite  from FileState where FilePath=:path COLLATE NOCASE", connection))
346
                {
347
                    
348
                    command.Parameters.AddWithValue("path", path);
349
                    
350
                    using (var reader = command.ExecuteReader())
351
                    {
352
                        if (reader.Read())
353
                        {
354
                            //var values = new object[reader.FieldCount];
355
                            //reader.GetValues(values);
356
                            var state = new FileState
357
                                            {
358
                                                Id = reader.GetGuid(0),
359
                                                FilePath = reader.IsDBNull(1)?"":reader.GetString(1),
360
                                                OverlayStatus =reader.IsDBNull(2)?FileOverlayStatus.Unversioned: (FileOverlayStatus) reader.GetInt64(2),
361
                                                FileStatus = reader.IsDBNull(3)?FileStatus.Missing:(FileStatus) reader.GetInt64(3),
362
                                                Checksum = reader.IsDBNull(4)?"":reader.GetString(4),
363
                                                Version = reader.IsDBNull(5)?default(long):reader.GetInt64(5),
364
                                                VersionTimeStamp = reader.IsDBNull(6)?default(DateTime):reader.GetDateTime(6),
365
                                                IsShared = !reader.IsDBNull(7) && reader.GetBoolean(7),
366
                                                SharedBy = reader.IsDBNull(8)?"":reader.GetString(8),
367
                                                ShareWrite = !reader.IsDBNull(9) && reader.GetBoolean(9)
368
                                            };
369
/*
370
                            var state = new FileState
371
                                            {
372
                                                Id = (Guid) values[0],
373
                                                FilePath = (string) values[1],
374
                                                OverlayStatus = (FileOverlayStatus) (long)values[2],
375
                                                FileStatus = (FileStatus) (long)values[3],
376
                                                Checksum = (string) values[4],
377
                                                Version = (long?) values[5],
378
                                                VersionTimeStamp = (DateTime?) values[6],
379
                                                IsShared = (long)values[7] == 1,
380
                                                SharedBy = (string) values[8],
381
                                                ShareWrite = (long)values[9] == 1
382
                                            };
383
*/
384
                            return state;
385
                        }
386
                        else
387
                        {
388
                            return null;
389
                        }
390

    
391
                    }                    
392
                }
393
            }
394
            catch (Exception exc)
395
            {
396
                Log.ErrorFormat(exc.ToString());
397
                throw;
398
            }            
399
        }
400

    
401
        public FileOverlayStatus GetFileOverlayStatus(string path)
402
        {
403
            if (String.IsNullOrWhiteSpace(path))
404
                throw new ArgumentNullException("path");
405
            if (!Path.IsPathRooted(path))
406
                throw new ArgumentException("The path must be rooted", "path");
407
            Contract.EndContractBlock();
408

    
409
            try
410
            {
411
                
412
                using (var connection = GetConnection())
413
                using (var command = new SQLiteCommand("select OverlayStatus from FileState where FilePath=:path  COLLATE NOCASE", connection))
414
                {
415
                    
416
                    command.Parameters.AddWithValue("path", path);
417
                    
418
                    var s = command.ExecuteScalar();
419
                    return (FileOverlayStatus) Convert.ToInt32(s);
420
                }
421
            }
422
            catch (Exception exc)
423
            {
424
                Log.ErrorFormat(exc.ToString());
425
                return FileOverlayStatus.Unversioned;
426
            }
427
        }
428

    
429
        private string GetConnectionString()
430
        {
431
            var connectionString = String.Format(@"Data Source={0}\pithos.db;Version=3;Enlist=N;Pooling=True", _pithosDataPath);
432
            return connectionString;
433
        }
434

    
435
        private SQLiteConnection GetConnection()
436
        {
437
            var connectionString = GetConnectionString();
438
            var connection = new SQLiteConnection(connectionString);
439
            connection.Open();
440
            using(var cmd =connection.CreateCommand())
441
            {
442
                cmd.CommandText = "PRAGMA journal_mode=WAL";
443
                cmd.ExecuteNonQuery();
444
            }
445
            return connection;
446
        }
447

    
448
        public void SetFileOverlayStatus(string path, FileOverlayStatus overlayStatus)
449
        {
450
            if (String.IsNullOrWhiteSpace(path))
451
                throw new ArgumentNullException("path");
452
            if (!Path.IsPathRooted(path))
453
                throw new ArgumentException("The path must be rooted","path");
454
            Contract.EndContractBlock();
455

    
456
            _persistenceAgent.Post(() => FileState.StoreOverlayStatus(path,overlayStatus));
457
        }
458

    
459
       /* public void RenameFileOverlayStatus(string oldPath, string newPath)
460
        {
461
            if (String.IsNullOrWhiteSpace(oldPath))
462
                throw new ArgumentNullException("oldPath");
463
            if (!Path.IsPathRooted(oldPath))
464
                throw new ArgumentException("The oldPath must be rooted", "oldPath");
465
            if (String.IsNullOrWhiteSpace(newPath))
466
                throw new ArgumentNullException("newPath");
467
            if (!Path.IsPathRooted(newPath))
468
                throw new ArgumentException("The newPath must be rooted", "newPath");
469
            Contract.EndContractBlock();
470

    
471
            _persistenceAgent.Post(() =>FileState.RenameState(oldPath, newPath));
472
        }*/
473

    
474
        public void SetFileState(string path, FileStatus fileStatus, FileOverlayStatus overlayStatus)
475
        {
476
            if (String.IsNullOrWhiteSpace(path))
477
                throw new ArgumentNullException("path");
478
            if (!Path.IsPathRooted(path))
479
                throw new ArgumentException("The path must be rooted", "path");
480
            Contract.EndContractBlock();
481

    
482
            Debug.Assert(!path.Contains(FolderConstants.CacheFolder));
483
            Debug.Assert(!path.EndsWith(".ignore"));
484

    
485
            _persistenceAgent.Post(() => UpdateStatusDirect(path, fileStatus, overlayStatus));
486
        }
487

    
488
/*
489
        public void StoreInfo(string path,ObjectInfo objectInfo)
490
        {
491
            if (String.IsNullOrWhiteSpace(path))
492
                throw new ArgumentNullException("path");
493
            if (!Path.IsPathRooted(path))
494
                throw new ArgumentException("The path must be rooted", "path");            
495
            if (objectInfo == null)
496
                throw new ArgumentNullException("objectInfo", "objectInfo can't be empty");
497
            Contract.EndContractBlock();
498

    
499
            _persistenceAgent.Post(() =>
500
            {
501
                var filePath = path.ToLower();
502
                //Load the existing files state and set its properties in one session            
503
                using (new SessionScope())
504
                {
505
                    //Forgetting to use a sessionscope results in two sessions being created, one by 
506
                    //FirstOrDefault and one by Save()
507
                    var state =FileState.FindByFilePath(filePath);
508
                    
509
                    //Create a new empty state object if this is a new file
510
                    state = state ?? new FileState();
511

    
512
                    state.FilePath = filePath;
513
                    state.Checksum = objectInfo.Hash;
514
                    state.Version = objectInfo.Version;
515
                    state.VersionTimeStamp = objectInfo.VersionTimestamp;
516

    
517
                    state.FileStatus = FileStatus.Unchanged;
518
                    state.OverlayStatus = FileOverlayStatus.Normal;
519
                    
520
                  
521
                    //Do the save
522
                    state.Save();
523
                }
524
            });
525

    
526
        }
527
*/
528
        
529
        public void StoreInfo(string path, ObjectInfo objectInfo)
530
        {
531
            if (String.IsNullOrWhiteSpace(path))
532
                throw new ArgumentNullException("path");
533
            if (!Path.IsPathRooted(path))
534
                throw new ArgumentException("The path must be rooted", "path");
535
            if (objectInfo == null)
536
                throw new ArgumentNullException("objectInfo", "objectInfo can't be empty");
537
            Contract.EndContractBlock();
538

    
539
            _persistenceAgent.Post(() => StoreInfoDirect(path, objectInfo));
540

    
541
        }
542

    
543
        private void StoreInfoDirect(string path, ObjectInfo objectInfo)
544
        {
545
            try
546
            {
547
                
548
                using (var connection = GetConnection())
549
                using (var command = new SQLiteCommand(connection))
550
                {
551
                    if (StateExists(path, connection))
552
                        command.CommandText =
553
                            "update FileState set FileStatus= :fileStatus where FilePath = :path  COLLATE NOCASE ";
554
                    else
555
                    {
556
                        command.CommandText =
557
                            "INSERT INTO FileState (Id,FilePath,Checksum,Version,VersionTimeStamp,FileStatus,OverlayStatus) VALUES (:id,:path,:checksum,:version,:versionTimeStamp,:fileStatus,:overlayStatus)";
558
                        command.Parameters.AddWithValue("id", Guid.NewGuid());
559
                    }
560

    
561
                    command.Parameters.AddWithValue("path", path);
562
                    command.Parameters.AddWithValue("checksum", objectInfo.Hash);
563
                    command.Parameters.AddWithValue("version", objectInfo.Version);
564
                    command.Parameters.AddWithValue("versionTimeStamp",
565
                                                    objectInfo.VersionTimestamp);
566
                    command.Parameters.AddWithValue("fileStatus", FileStatus.Unchanged);
567
                    command.Parameters.AddWithValue("overlayStatus",
568
                                                    FileOverlayStatus.Normal);
569

    
570
                    var affected = command.ExecuteNonQuery();
571
                    return;
572
                }
573
            }
574
            catch (Exception exc)
575
            {
576
                Log.Error(exc.ToString());
577
                throw;
578
            }
579
        }
580

    
581
        private bool StateExists(string filePath,SQLiteConnection connection)
582
        {
583
            using (var command = new SQLiteCommand("Select count(*) from FileState where FilePath=:path  COLLATE NOCASE", connection))
584
            {
585
                command.Parameters.AddWithValue("path", filePath);
586
                var result = command.ExecuteScalar();
587
                return ((long)result >= 1);
588
            }
589

    
590
        }
591

    
592
        public void SetFileStatus(string path, FileStatus status)
593
        {
594
            if (String.IsNullOrWhiteSpace(path))
595
                throw new ArgumentNullException("path");
596
            if (!Path.IsPathRooted(path))
597
                throw new ArgumentException("The path must be rooted", "path");
598
            Contract.EndContractBlock();
599
            
600
            _persistenceAgent.Post(() => UpdateStatusDirect(path, status));
601
        }
602

    
603
        public FileStatus GetFileStatus(string path)
604
        {
605
            if (String.IsNullOrWhiteSpace(path))
606
                throw new ArgumentNullException("path");
607
            if (!Path.IsPathRooted(path))
608
                throw new ArgumentException("The path must be rooted", "path");
609
            Contract.EndContractBlock();
610

    
611
            
612
            using (var connection = GetConnection())
613
            {
614
                var command = new SQLiteCommand("select FileStatus from FileState where FilePath=:path  COLLATE NOCASE", connection);
615
                command.Parameters.AddWithValue("path", path);
616
                
617
                var s = command.ExecuteScalar();
618
                return (FileStatus)Convert.ToInt32(s);
619
            }
620
        }
621

    
622
        /// <summary>
623
        /// Deletes the status of the specified file
624
        /// </summary>
625
        /// <param name="path"></param>
626
        public void ClearFileStatus(string path)
627
        {
628
            if (String.IsNullOrWhiteSpace(path))
629
                throw new ArgumentNullException("path");
630
            if (!Path.IsPathRooted(path))
631
                throw new ArgumentException("The path must be rooted", "path");
632
            Contract.EndContractBlock();
633

    
634
            _persistenceAgent.Post(() => DeleteDirect(path));   
635
        }
636

    
637
        /// <summary>
638
        /// Deletes the status of the specified folder and all its contents
639
        /// </summary>
640
        /// <param name="path"></param>
641
        public void ClearFolderStatus(string path)
642
        {
643
            if (String.IsNullOrWhiteSpace(path))
644
                throw new ArgumentNullException("path");
645
            if (!Path.IsPathRooted(path))
646
                throw new ArgumentException("The path must be rooted", "path");
647
            Contract.EndContractBlock();
648

    
649
            _persistenceAgent.Post(() => DeleteFolderDirect(path));   
650
        }
651

    
652
        public IEnumerable<FileState> GetChildren(FileState fileState)
653
        {
654
            if (fileState == null)
655
                throw new ArgumentNullException("fileState");
656
            Contract.EndContractBlock();
657

    
658
            var children = from state in FileState.Queryable
659
                           where state.FilePath.StartsWith(fileState.FilePath + "\\")
660
                           select state;
661
            return children;
662
        }
663

    
664
        private int DeleteDirect(string filePath)
665
        {
666
            using (log4net.ThreadContext.Stacks["StatusAgent"].Push("DeleteDirect"))
667
            {
668

    
669
                try
670
                {
671

    
672
                    
673
                    using (var connection = GetConnection())
674
                    {
675
                        var command = new SQLiteCommand("delete from FileState where FilePath = :path  COLLATE NOCASE",
676
                                                        connection);
677

    
678
                        command.Parameters.AddWithValue("path", filePath);
679
                        
680
                        var affected = command.ExecuteNonQuery();
681
                        return affected;
682
                    }
683
                }
684
                catch (Exception exc)
685
                {
686
                    Log.Error(exc.ToString());
687
                    throw;
688
                }
689
            }
690
        }
691

    
692
        private int DeleteFolderDirect(string filePath)
693
        {
694
            using (log4net.ThreadContext.Stacks["StatusAgent"].Push("DeleteDirect"))
695
            {
696

    
697
                try
698
                {
699

    
700
                    
701
                    using (var connection = GetConnection())
702
                    {
703
                        var command = new SQLiteCommand("delete from FileState where FilePath = :path or FilePath like :path + '/%'  COLLATE NOCASE",
704
                                                        connection);
705

    
706
                        command.Parameters.AddWithValue("path", filePath);
707
                        
708
                        var affected = command.ExecuteNonQuery();
709
                        return affected;
710
                    }
711
                }
712
                catch (Exception exc)
713
                {
714
                    Log.Error(exc.ToString());
715
                    throw;
716
                }
717
            }
718
        }
719

    
720
        public void UpdateFileChecksum(string path, string checksum)
721
        {
722
            if (String.IsNullOrWhiteSpace(path))
723
                throw new ArgumentNullException("path");
724
            if (!Path.IsPathRooted(path))
725
                throw new ArgumentException("The path must be rooted", "path");            
726
            Contract.EndContractBlock();
727

    
728
            _persistenceAgent.Post(() => FileState.UpdateChecksum(path, checksum));
729
        }
730

    
731
    }
732

    
733
   
734
}