コード例 #1
0
        /// <summary>
        /// Sort sources by root volume
        /// </summary>
        /// <param name="sources">List of sources</param>
        /// <returns>Dictionary of volumes, with list of sources as values</returns>
        private static Dictionary <string, List <string> > SortByVolume(IEnumerable <string> sources)
        {
            var sourcesByVolume = new Dictionary <string, List <string> >();

            foreach (var path in sources)
            {
                // get NTFS volume root
                var volumeRoot = USNJournal.GetVolumeRootFromPath(path);

                if (!sourcesByVolume.TryGetValue(volumeRoot, out var list))
                {
                    list = new List <string>();
                    sourcesByVolume.Add(volumeRoot, list);
                }

                list.Add(path);
            }

            return(sourcesByVolume);
        }
コード例 #2
0
        /// <summary>
        /// Returns true if path was enumerated by journal service
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        public bool IsPathEnumerated(string path)
        {
            // get NTFS volume root
            var volumeRoot = USNJournal.GetVolumeRootFromPath(path);

            // get volume data
            if (!m_volumeDataDict.TryGetValue(volumeRoot, out var volumeData))
            {
                return(false);
            }

            if (volumeData.IsFullScan)
            {
                return(true); // do not append from previous set, already scanned
            }
            if (volumeData.Files.Contains(path))
            {
                return(true); // do not append from previous set, already scanned
            }
            foreach (var folder in volumeData.Folders)
            {
                if (m_token.IsCancellationRequested)
                {
                    break;
                }

                if (path.Equals(folder, Utility.Utility.ClientFilenameStringComparison))
                {
                    return(true); // do not append from previous set, already scanned
                }
                if (Utility.Utility.IsPathBelowFolder(path, folder))
                {
                    return(true); // do not append from previous set, already scanned
                }
            }

            return(false); // append from previous set
        }
コード例 #3
0
        /// <summary>
        /// Initialize list of modified files / folder for each volume
        /// </summary>
        /// <param name="emitFilter"></param>
        /// <param name="fileAttributeFilter"></param>
        /// <param name="skipFilesLargerThan"></param>
        /// <param name="prevJournalData"></param>
        /// <returns></returns>
        private Dictionary <string, VolumeData> Initialize(IFilter emitFilter, FileAttributes fileAttributeFilter, long skipFilesLargerThan,
                                                           IEnumerable <USNJournalDataEntry> prevJournalData)
        {
            if (prevJournalData == null)
            {
                throw new UsnJournalSoftFailureException(Strings.USNHelper.PreviousBackupNoInfo);
            }

            var result = new Dictionary <string, VolumeData>();

            // get hash identifying current source filter / sources configuration
            // ReSharper disable once PossibleMultipleEnumeration
            var configHash = Utility.Utility.ByteArrayAsHexString(MD5HashHelper.GetHash(new string[] {
                emitFilter == null ? string.Empty : emitFilter.ToString(),
                string.Join("; ", m_sources),
                fileAttributeFilter.ToString(),
                skipFilesLargerThan.ToString()
            }));

            // create lookup for journal data
            var journalDataDict = prevJournalData.ToDictionary(data => data.Volume);

            // iterate over volumes
            foreach (var sourcesPerVolume in SortByVolume(m_sources))
            {
                if (m_token.IsCancellationRequested)
                {
                    break;
                }

                var volume        = sourcesPerVolume.Key;
                var volumeSources = sourcesPerVolume.Value;
                var volumeData    = new VolumeData
                {
                    Volume      = volume,
                    JournalData = null
                };
                result[volume] = volumeData;

                try
                {
                    // prepare journal data entry to store with current fileset
                    var journal  = new USNJournal(volume);
                    var nextData = new USNJournalDataEntry
                    {
                        Volume     = volume,
                        JournalId  = journal.JournalId,
                        NextUsn    = journal.NextUsn,
                        ConfigHash = configHash
                    };

                    // add new data to result set
                    volumeData.JournalData = nextData;

                    // only use change journal if:
                    // - journal ID hasn't changed
                    // - nextUsn isn't zero (we use this as magic value to force a rescan)
                    // - the configuration (sources or filters) hasn't changed
                    if (!journalDataDict.TryGetValue(volume, out var prevData))
                    {
                        throw new UsnJournalSoftFailureException(Strings.USNHelper.PreviousBackupNoInfo);
                    }

                    if (prevData.JournalId != nextData.JournalId)
                    {
                        throw new UsnJournalSoftFailureException(Strings.USNHelper.JournalIdChanged);
                    }

                    if (prevData.NextUsn == 0)
                    {
                        throw new UsnJournalSoftFailureException(Strings.USNHelper.NextUsnZero);
                    }

                    if (prevData.ConfigHash != nextData.ConfigHash)
                    {
                        throw new UsnJournalSoftFailureException(Strings.USNHelper.ConfigHashChanged);
                    }

                    var changedFiles   = new HashSet <string>(Utility.Utility.ClientFilenameStringComparer);
                    var changedFolders = new HashSet <string>(Utility.Utility.ClientFilenameStringComparer);

                    // obtain changed files and folders, per volume
                    foreach (var source in volumeSources)
                    {
                        if (m_token.IsCancellationRequested)
                        {
                            break;
                        }

                        foreach (var entry in journal.GetChangedFileSystemEntries(source, prevData.NextUsn))
                        {
                            if (m_token.IsCancellationRequested)
                            {
                                break;
                            }

                            if (entry.Item2.HasFlag(USNJournal.EntryType.File))
                            {
                                changedFiles.Add(entry.Item1);
                            }
                            else
                            {
                                changedFolders.Add(Util.AppendDirSeparator(entry.Item1));
                            }
                        }
                    }

                    // At this point we have:
                    //  - a list of folders (changedFolders) that were possibly modified
                    //  - a list of files (changedFiles) that were possibly modified
                    //
                    // With this, we need still need to do the following:
                    //
                    // 1. Simplify the folder list, such that it only contains the parent-most entries
                    //     (eg. { "C:\A\B\", "C:\A\B\C\", "C:\A\B\D\E\" } => { "C:\A\B\" }
                    volumeData.Folders = Utility.Utility.SimplifyFolderList(changedFolders).ToList();

                    // 2. Our list of files may contain entries inside one of the simplified folders (from step 1., above).
                    //    Since that folder is going to be fully scanned, those files can be removed.
                    //    Note: it would be wrong to use the result from step 2. as the folder list! The entries removed
                    //          between 1. and 2. are *excluded* folders, and files below them are to be *excluded*, too.
                    volumeData.Files =
                        new HashSet <string>(Utility.Utility.GetFilesNotInFolders(changedFiles, volumeData.Folders));

                    // Record success for volume
                    volumeData.IsFullScan = false;
                }
                catch (Exception e)
                {
                    // full scan is required this time (eg. due to missing journal entries)
                    volumeData.Exception  = e;
                    volumeData.IsFullScan = true;
                    volumeData.Folders    = new List <string>();
                    volumeData.Files      = new HashSet <string>();

                    // use original sources
                    foreach (var path in volumeSources)
                    {
                        var isFolder = path.EndsWith(Util.DirectorySeparatorString, StringComparison.Ordinal);
                        if (isFolder)
                        {
                            volumeData.Folders.Add(path);
                        }
                        else
                        {
                            volumeData.Files.Add(path);
                        }
                    }
                }
            }

            return(result);
        }