Revision 2edb4807 trunk/Pithos.Core/Agents/NetworkAgent.cs
b/trunk/Pithos.Core/Agents/NetworkAgent.cs | ||
---|---|---|
642 | 642 |
} |
643 | 643 |
} |
644 | 644 |
|
645 |
internal class ObjectInfoComparer:EqualityComparer<ObjectInfo> |
|
646 |
{ |
|
647 |
public override bool Equals(ObjectInfo x, ObjectInfo y) |
|
648 |
{ |
|
649 |
return (x.Account == y.Account && |
|
650 |
x.Container == y.Container && |
|
651 |
x.Name == y.Name); |
|
652 |
} |
|
653 |
|
|
654 |
public override int GetHashCode(ObjectInfo obj) |
|
655 |
{ |
|
656 |
return String.Join("/",obj.Account,obj.Container,obj.Name).GetHashCode(); |
|
657 |
} |
|
658 |
} |
|
659 |
|
|
660 |
Dictionary<string, List<ObjectInfo>> _currentSnapshot = new Dictionary<string, List<ObjectInfo>>(); |
|
661 |
Dictionary<string, List<ObjectInfo>> _previousSnapshot = new Dictionary<string, List<ObjectInfo>>(); |
|
662 |
|
|
645 | 663 |
/// <summary> |
646 | 664 |
/// Deletes local files that are not found in the list of cloud files |
647 | 665 |
/// </summary> |
... | ... | |
658 | 676 |
throw new ArgumentNullException("cloudFiles"); |
659 | 677 |
Contract.EndContractBlock(); |
660 | 678 |
|
661 |
//Check the Modified date to ensure that were just created and haven't been uploaded yet
|
|
662 |
//NOTE: The NHibernate LINQ provider doesn't support custom functions so we need to break the query
|
|
663 |
//in two steps
|
|
664 |
//NOTE: DON'T return files that are already in conflict. The first poll would mark them as
|
|
665 |
//"In Conflict" but subsequent polls would delete them
|
|
666 |
/* var t=FileState.Find(new Guid("{cd664c9a-5f17-47c9-b27f-3bcbcb0595ff}"));
|
|
679 |
if (_previousSnapshot.ContainsKey(accountInfo.UserName) && _currentSnapshot.ContainsKey(accountInfo.UserName))
|
|
680 |
_previousSnapshot[accountInfo.UserName] = _currentSnapshot[accountInfo.UserName] ?? new List<ObjectInfo>();
|
|
681 |
else
|
|
682 |
{
|
|
683 |
_previousSnapshot[accountInfo.UserName]=new List<ObjectInfo>();
|
|
684 |
}
|
|
667 | 685 |
|
668 |
var d0 = FileState.Queryable |
|
669 |
.Where(state => |
|
670 |
state.FilePath.StartsWith(accountInfo.AccountPath)).ToList(); |
|
671 |
|
|
672 |
var d1 = FileState.Queryable |
|
673 |
.Where(state => state.Modified <= pollTime).ToList(); |
|
674 |
var d2= FileState.Queryable |
|
675 |
.Where(state => state.Modified <= pollTime |
|
676 |
&& |
|
677 |
state.FilePath.StartsWith(accountInfo.AccountPath)).ToList();*/ |
|
678 |
|
|
679 |
//Consider for deleteion only files modified before the PREVIOUS poll |
|
680 |
//A user may perform a file creation or rename at roughly the same time as a poll. In such a case |
|
681 |
//the new file will appear as deleted |
|
682 |
var previousPollTime = pollTime.Subtract(TimeSpan.FromMilliseconds(Settings.PollingInterval)); |
|
683 |
|
|
684 |
//Only consider files that are not being modified, ie they are in the Unchanged state |
|
685 |
var deleteCandidates = FileState.Queryable.Where(state => |
|
686 |
state.Modified <= previousPollTime |
|
687 |
&& state.FilePath.StartsWith(accountInfo.AccountPath) |
|
688 |
&& state.FileStatus == FileStatus.Unchanged).ToList(); |
|
689 |
|
|
690 |
//TODO: filesToDelete must take into account the Others container |
|
691 |
var filesToDelete = (from deleteCandidate in deleteCandidates |
|
692 |
let localFile = FileInfoExtensions.FromPath(deleteCandidate.FilePath) |
|
693 |
let relativeFilePath = localFile.AsRelativeTo(accountInfo.AccountPath) |
|
694 |
let agentActions = _agent.GetEnumerable() |
|
695 |
where |
|
696 |
!_lastSeen.ContainsKey(localFile.FullName) |
|
697 |
&& !cloudFiles.Any(r => r.RelativeUrlToFilePath(accountInfo.UserName) == relativeFilePath ) |
|
698 |
//Exclude files enqueued for uploading |
|
699 |
//Large files will not appear on the server for multiple polls. They must not be marked as deleted |
|
700 |
&& !agentActions.Any(action => action.LocalFile.WithProperCapitalization().FullName == localFile.FullName) |
|
701 |
//Do NOT delete files modified since the previous poll |
|
702 |
&& localFile.LastAccessTime < previousPollTime |
|
703 |
select localFile).ToList(); |
|
704 |
|
|
686 |
_currentSnapshot[accountInfo.UserName] = cloudFiles.ToList(); |
|
687 |
|
|
688 |
var deletedObjects = _previousSnapshot[accountInfo.UserName].Except(_currentSnapshot[accountInfo.UserName], new ObjectInfoComparer()).ToList(); |
|
705 | 689 |
|
690 |
|
|
706 | 691 |
//On the first run |
707 | 692 |
if (_firstPoll) |
708 | 693 |
{ |
694 |
//Only consider files that are not being modified, ie they are in the Unchanged state |
|
695 |
var deleteCandidates = FileState.Queryable.Where(state => |
|
696 |
state.FilePath.StartsWith(accountInfo.AccountPath) |
|
697 |
&& state.FileStatus == FileStatus.Unchanged).ToList(); |
|
698 |
|
|
699 |
|
|
700 |
//TODO: filesToDelete must take into account the Others container |
|
701 |
var filesToDelete = (from deleteCandidate in deleteCandidates |
|
702 |
let localFile = FileInfoExtensions.FromPath(deleteCandidate.FilePath) |
|
703 |
let relativeFilePath = localFile.AsRelativeTo(accountInfo.AccountPath) |
|
704 |
where |
|
705 |
!cloudFiles.Any(r => r.RelativeUrlToFilePath(accountInfo.UserName) == relativeFilePath) |
|
706 |
select localFile).ToList(); |
|
707 |
|
|
708 |
|
|
709 |
|
|
709 | 710 |
//Set the status of missing files to Conflict |
710 | 711 |
foreach (var item in filesToDelete) |
711 | 712 |
{ |
... | ... | |
720 | 721 |
} |
721 | 722 |
UpdateStatus(PithosStatus.HasConflicts); |
722 | 723 |
StatusNotification.NotifyConflicts(filesToDelete, String.Format("{0} local files are missing from Pithos, possibly because they were deleted",filesToDelete.Count)); |
724 |
StatusNotification.NotifyForFiles(filesToDelete, String.Format("{0} files were deleted", filesToDelete.Count), TraceLevel.Info); |
|
723 | 725 |
} |
724 | 726 |
else |
725 | 727 |
{ |
726 |
foreach (var item in filesToDelete) |
|
728 |
var deletedFiles = new List<FileSystemInfo>(); |
|
729 |
foreach (var objectInfo in deletedObjects) |
|
727 | 730 |
{ |
731 |
var relativePath = objectInfo.RelativeUrlToFilePath(accountInfo.UserName); |
|
732 |
var item = GetFileAgent(accountInfo).GetFileSystemInfo(relativePath); |
|
728 | 733 |
if (item.Exists) |
729 | 734 |
{ |
730 | 735 |
//Try to acquire a gate on the file, to take into account files that have been dequeued |
731 | 736 |
//and are being processed |
732 | 737 |
//TODO: The gate is not enough. Perhaps we need to keep a journal of processed files and check against |
733 | 738 |
//that as well. |
739 |
/* |
|
734 | 740 |
using (var gate = NetworkGate.Acquire(item.FullName, NetworkOperation.Deleting)) |
735 | 741 |
{ |
736 | 742 |
if (gate.Failed) |
737 | 743 |
continue; |
744 |
*/ |
|
738 | 745 |
if ((item.Attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly) |
739 | 746 |
{ |
740 | 747 |
item.Attributes = item.Attributes & ~FileAttributes.ReadOnly; |
741 | 748 |
|
742 | 749 |
} |
743 | 750 |
item.Delete(); |
751 |
DateTime lastDate; |
|
752 |
_lastSeen.TryRemove(item.FullName, out lastDate); |
|
753 |
deletedFiles.Add(item); |
|
754 |
/* |
|
744 | 755 |
} |
756 |
*/ |
|
745 | 757 |
} |
746 | 758 |
StatusKeeper.ClearFileStatus(item.FullName); |
759 |
|
|
747 | 760 |
} |
748 |
StatusNotification.NotifyForFiles(filesToDelete, String.Format("{0} files were deleted",filesToDelete.Count),TraceLevel.Info);
|
|
761 |
StatusNotification.NotifyForFiles(deletedFiles, String.Format("{0} files were deleted", deletedFiles.Count), TraceLevel.Info);
|
|
749 | 762 |
} |
750 | 763 |
|
751 | 764 |
} |
... | ... | |
1236 | 1249 |
|
1237 | 1250 |
} |
1238 | 1251 |
|
1252 |
//Returns true if an action concerns a file that was deleted |
|
1239 | 1253 |
private bool IsDeletedFile(CloudAction action) |
1240 |
{ |
|
1254 |
{ |
|
1255 |
//Doesn't work for actions targeting shared files |
|
1256 |
if (action.IsShared) |
|
1257 |
return false; |
|
1241 | 1258 |
var key = GetFileKey(action.CloudFile); |
1242 | 1259 |
DateTime entryDate; |
1243 | 1260 |
if (_deletedFiles.TryGetValue(key, out entryDate)) |
Also available in: Unified diff