示例#1
0
        public static SyncLog GetLog(string path, bool whatIf)
        {
            SyncLog syncLog = new SyncLog(path)
            {
                WhatIf = whatIf
            };

            syncLog.Reload();
            return(syncLog);
        }
示例#2
0
        /// <summary>
        /// Matches files from Encr, Decr and Log Entry
        /// </summary>
        private List <ChangeBuilder> FindChanges_St04_ThreeWayJoin(List <FSEntry> encrDirectoryFiles, List <FSEntry> decrDirectoryFiles, SyncLog syncLog, ConsoleEx console)
        {
            List <ChangeBuilder> preSyncDetails = new List <ChangeBuilder>();

            //Adds Logs
            console?.WriteLine(VerbosityLevel.Diagnostic, 2, "Merging in log...");
            preSyncDetails.AddRange(syncLog.Select(entry => new ChangeBuilder {
                LogEntry = entry
            }));
            console?.WriteLine(VerbosityLevel.Diagnostic, 3, $"{preSyncDetails.Count} added");

            //Updates/Adds Decrypted File Information
            console?.WriteLine(VerbosityLevel.Diagnostic, 2, "Merging in decrypted information...");
            int decrStatAdd   = 0;
            int decrStatMerge = 0;
            var decrJoin      = decrDirectoryFiles
                                .GroupJoin(preSyncDetails,
                                           o => o.RelativePath,
                                           i => i?.LogEntry?.DecrFileName,
                                           (o, i) => new Tuple <FSEntry, ChangeBuilder>(o, i.FirstOrDefault()));

            foreach (var entry in decrJoin.ToList())
            {
                if (entry.Item2 == null)
                {
                    //New Entry (not in log)
                    preSyncDetails.Add(new ChangeBuilder {
                        DecrInfo = entry.Item1
                    });
                    decrStatAdd++;
                }
                else
                {
                    //Existing Entry (update only)
                    entry.Item2.DecrInfo = entry.Item1;
                    if (entry.Item1 != null)
                    {
                        decrStatMerge++;
                    }
                }
            }
            console?.WriteLine(VerbosityLevel.Diagnostic, 3, $"{decrStatAdd} added, {decrStatMerge} merged");

            //find encrypted file names
            foreach (var entry in preSyncDetails)
            {
                entry.DecrFileName = entry.LogEntry?.DecrFileName ?? entry.DecrInfo.RelativePath;
                entry.EncrFileName = FileNameEncoder.EncodeName(entry.DecrFileName);
            }


            //Updates/adds encrypted File Information
            console?.WriteLine(VerbosityLevel.Diagnostic, 2, "Merging in encrypted information...");
            int encrStatAdd   = 0;
            int encrStatMerge = 0;
            var encrJoin      = encrDirectoryFiles
                                .GroupJoin(preSyncDetails,
                                           o => o.RelativePath,
                                           i => i.EncrFileName,
                                           (o, i) => new Tuple <FSEntry, ChangeBuilder>(o, i.FirstOrDefault()));

            foreach (var entry in encrJoin.ToList())
            {
                if (entry.Item2 == null)
                {
                    //New Entry (not in log or decrypted file)
                    preSyncDetails.Add(new ChangeBuilder {
                        EncrInfo = entry.Item1, EncrFileName = entry.Item1.RelativePath
                    });
                    encrStatAdd++;
                }
                else
                {
                    //Existing Entry
                    entry.Item2.EncrInfo = entry.Item1;
                    if (entry.Item1 != null)
                    {
                        encrStatMerge++;
                    }
                }
            }
            console?.WriteLine(VerbosityLevel.Diagnostic, 3, $"{encrStatAdd} added, {encrStatMerge} merged");

            return(preSyncDetails);
        }
示例#3
0
        /// <summary>
        /// Returns a list of changes that need to be performed as part of the sync.
        /// </summary>
        public List <PreSyncDetails> FindChanges(bool clearCache = true, ConsoleEx console = null)
        {
            //todo: disable default for reset
            console?.WriteLine(VerbosityLevel.Diagnostic, 0, "Finding Changes...");
            if (clearCache)
            {
                ClearCache();
            }

            using var rng = RandomNumberGenerator.Create();

            console?.WriteLine(VerbosityLevel.Diagnostic, 1, "Enumerating Encr Directory...");
            List <FSEntry> encrDirectoryFiles = EncrDirectory.GetEntries(SearchOption.AllDirectories).Where(EncrFilter).ToList();

            console?.WriteLine(VerbosityLevel.Diagnostic, 2, $"{encrDirectoryFiles.Count} files found");

            console?.WriteLine(VerbosityLevel.Diagnostic, 1, $"Enumerating Decr Directory...");
            console?.WriteLine(VerbosityLevel.Diagnostic, 2, $"Path: {DecrDirectory.FullName}");
            List <FSEntry> decrDirectoryFiles = DecrDirectory.GetEntries(SearchOption.AllDirectories).Where(DecrFilter).ToList();

            console?.WriteLine(VerbosityLevel.Diagnostic, 2, $"{decrDirectoryFiles.Count} files found");

            console?.WriteLine(VerbosityLevel.Diagnostic, 1, "Reading sync log (decr side)...");
            console?.WriteLine(VerbosityLevel.Diagnostic, 2, $"{SyncLog.Count()} entries found");
            //todo: filter out log entries where the decr file name and the encr file name does not match


            console?.WriteLine(VerbosityLevel.Diagnostic, 1, "Performing 3 way join...");
            List <ChangeBuilder> changes = FindChanges_St04_ThreeWayJoin(encrDirectoryFiles, decrDirectoryFiles, SyncLog, console).ToList();


            console?.WriteLine(VerbosityLevel.Diagnostic, 1, "Refreshing EncrHeaders...");
            FindChanges_St05_RefreshEncrHeaders(console, changes);


            //todo: detect conflicts

            console?.WriteLine(VerbosityLevel.Diagnostic, 1, "Adding Relationships...");
            FindChanges_St06_AddRelationships(changes);

#if DEBUG
            Debug.Assert(changes.All(c => c.RelationParents != null));
            Debug.Assert(changes.All(c => c.RelationChildren != null));
            Debug.Assert(changes.All(c => c.RelationCaseDifference != null));
#endif


            console?.WriteLine(VerbosityLevel.Diagnostic, 1, "Calculating Operation...");
            FindChanges_St07_CalculateOperation(changes);

            console?.WriteLine(VerbosityLevel.Diagnostic, 1, "Calculating Sync Mode...");
            FindChanges_St08_CalculateSyncMode(changes);

            console?.WriteLine(VerbosityLevel.Diagnostic, 1, "Calculating Dependencies...");
            FindChanges_St09_CalculateDependencies(changes);

            console?.WriteLine(VerbosityLevel.Diagnostic, 1, "Sorting...");
            changes = FindChanges_St10_Sort(rng, changes);



            return(changes
                   .Where(m => m.SyncMode != PreSyncMode.Unchanged)
                   .Select(m => m.ToSyncEntry())
                   .ToList());
        }
示例#4
0
 public void Dispose()
 {
     SyncLog?.Dispose();
     SyncLog = null;
 }
示例#5
0
        public SyncResults TrySync(PreSyncDetails entry, ConsoleEx console = null)
        {
            const int encrTimespanPrecisionMS = 1000;

            //todo: ensure the entry direction and operation end up being the same
            //todo: ensure all the calling methods do something with the results

            //if (WhatIf)
            //    throw new InvalidOperationException("Unable to perform sync when WhatIf mode is set to true");
            if (entry == null)
            {
                throw new ArgumentNullException(nameof(entry));
            }

            if (entry.SyncMode == PreSyncMode.DecryptedSide)
            {
                SyncLogEntry logEntry = entry.LogEntry;


                string             encrPath = Path.Combine(EncrDirectory.FullName, HelixUtil.PathNative(entry.EncrFileName));
                string             decrPath = Path.Combine(DecrDirectory.FullName, HelixUtil.PathNative(entry.DecrFileName));
                FileEncryptOptions options  = new FileEncryptOptions();
                FileEntry          header   = null;
                options.BeforeWriteHeader = (h) => header = h;
                options.StoredFileName    = entry.DecrFileName;
                options.FileVersion       = Header.FileVersion;
                options.Log = (s) => console?.WriteLine(VerbosityLevel.Diagnostic, 1, s);

                if (WhatIf)
                {
                    EncrDirectory.WhatIfReplaceFile(encrPath, entry.DisplayFileLength);
                    if (entry.DecrInfo == null)
                    {
                        header = new FileEntry()
                        {
                            EntryType = FileEntryType.Removed,
                            FileName  = entry.DecrFileName,
                        };
                    }
                    else
                    {
                        header = entry.DecrInfo.ToFileEntry();
                    }
                }
                else
                {
                    HelixFile.Encrypt(decrPath, encrPath, DerivedBytesProvider, options);


                    //forces a change if the file was modified to quickly
                    if (logEntry != null && (File.GetLastWriteTimeUtc(encrPath) - logEntry.EncrModified).TotalMilliseconds < encrTimespanPrecisionMS)
                    {
                        File.SetLastWriteTimeUtc(encrPath, logEntry.EncrModified + TimeSpan.FromMilliseconds(encrTimespanPrecisionMS));
                    }

                    EncrDirectory.RefreshEntry(encrPath);
                }
                var newLogEntry = CreateEntryFromHeader(header, EncrDirectory.TryGetEntry(entry.EncrFileName));
                SyncLog.Add(newLogEntry);


                return(SyncResults.Success());
            }
            else if (entry.SyncMode == PreSyncMode.EncryptedSide)
            {
                if (entry.DisplayOperation == PreSyncOperation.Purge)
                {
                    SyncLog.Add(entry.GetUpdatedLogEntry());
                    return(SyncResults.Success());
                }
                else
                {
                    //todo: use the DisplayOperation to determine what to do (ensures the counts stay consistant)

                    SyncLogEntry logEntry        = entry.LogEntry;
                    SyncLogEntry fileSystemEntry = CreateNewLogEntryFromEncrPath(entry.EncrFileName);
                    if (logEntry?.ToString() == fileSystemEntry?.ToString())
                    {
                        return(SyncResults.Success()); //Unchanged
                    }
                    //todo: test to see if there are illegal characters
                    //todo: check if the name matches

                    string encrPath = HelixUtil.JoinNative(EncrDirectory.FullName, fileSystemEntry.EncrFileName);
                    string decrPath = HelixUtil.JoinNative(DecrDirectory.FullName, fileSystemEntry.DecrFileName);

                    //todo: if file exists with different case - skip file
                    var     exactPath = HelixUtil.GetExactPathName(decrPath);
                    FSEntry decrEntry = DecrDirectory.TryGetEntry(fileSystemEntry.DecrFileName);

                    if (decrEntry != null && decrEntry.RelativePath != fileSystemEntry.DecrFileName)
                    {
                        //todo: throw more specific exception
                        return(SyncResults.Failure(new HelixException($"Case only conflict file \"{decrPath}\" exists as \"{exactPath}\".")));
                    }

                    if (WhatIf)
                    {
                        if (entry.EncrHeader.EntryType == FileEntryType.File)
                        {
                            DecrDirectory.WhatIfReplaceFile(decrPath, entry.EncrHeader.Length, entry.EncrHeader.LastWriteTimeUtc);
                        }
                        else if (entry.EncrHeader.EntryType == FileEntryType.Removed)
                        {
                            DecrDirectory.WhatIfDeleteFile(decrPath);
                        }
                        else if (entry.EncrHeader.EntryType == FileEntryType.Directory)
                        {
                            DecrDirectory.WhatIfAddDirectory(decrPath);
                        }
                        else
                        {
                            throw new NotSupportedException();
                        }
                    }
                    else
                    {
                        HelixFile.Decrypt(encrPath, decrPath, DerivedBytesProvider);
                        //todo: get the date on the file system (needed if the filesystem has less precision
                        DecrDirectory.RefreshEntry(decrPath);
                    }

                    SyncLog.Add(fileSystemEntry);

                    return(SyncResults.Success());
                }
            }
            else if (entry.SyncMode == PreSyncMode.Match)
            {
                //Add to Log file (changed to be equal on both sides)
                SyncLogEntry fileSystemEntry = CreateNewLogEntryFromDecrPath(entry.DecrFileName);
                SyncLog.Add(fileSystemEntry);
                return(SyncResults.Success());
            }
            else if (entry.SyncMode == PreSyncMode.Unchanged)
            {
                //do nothing
                return(SyncResults.Success());
            }

            return(SyncResults.Failure(new HelixException($"Invalid sync mode {entry.SyncMode}")));
        }
示例#6
0
 public void ClearCache()
 {
     EncrDirectory.Reset();
     SyncLog.Reload();
     DecrDirectory.Reset();
 }
示例#7
0
 public void OpenDecr(ConsoleEx consoleEx)
 {
     SyncLog = SyncLog.GetLog(DecrDirectory.PathFull(SyncLogPath), WhatIf);
 }