/// <summary> /// Filter supplied <c>folders</c>, removing any folder which itself, or one /// of its ancestors, is excluded by the <c>filter</c>. /// </summary> /// <param name="folders">Folder to filter</param> /// <param name="filter">Exclusion filter</param> /// <param name="cache">Cache of excluded folders (optional)</param> /// <param name="errorCallback"></param> /// <returns>Filtered folders</returns> private IEnumerable <string> FilterExcludedFolders(IEnumerable <string> folders, Utility.Utility.EnumerationFilterDelegate filter, IDictionary <string, bool> cache, Utility.Utility.ReportAccessError errorCallback = null) { var result = new List <string>(); foreach (var folder in folders) { if (m_token.IsCancellationRequested) { break; } try { if (!IsFolderOrAncestorsExcluded(folder, filter, cache)) { result.Add(folder); } } catch (System.Threading.ThreadAbortException) { throw; } catch (Exception ex) { errorCallback?.Invoke(folder, folder, ex); filter(folder, folder, FileAttributes.Directory | Utility.Utility.ATTRIBUTE_ERROR); } } return(result); }
/// <summary> /// Filter supplied <c>files</c>, removing any files which itself, or one /// of its parent folders, is excluded by the <c>filter</c>. /// </summary> /// <param name="files">Files to filter</param> /// <param name="filter">Exclusion filter</param> /// <param name="cache">Cache of included and exculded files / folders</param> /// <param name="errorCallback"></param> /// <returns>Filtered files</returns> private IEnumerable <string> FilterExcludedFiles(IEnumerable <string> files, Utility.Utility.EnumerationFilterDelegate filter, IDictionary <string, bool> cache, Utility.Utility.ReportAccessError errorCallback = null) { var result = new List <string>(); foreach (var file in files) { var attr = m_snapshot.FileExists(file) ? m_snapshot.GetAttributes(file) : FileAttributes.Normal; try { if (!filter(file, file, attr)) { continue; } if (!IsFolderOrAncestorsExcluded(Utility.Utility.GetParent(file, true), filter, cache)) { result.Add(file); } } catch (System.Threading.ThreadAbortException) { throw; } catch (Exception ex) { errorCallback?.Invoke(file, file, ex); filter(file, file, attr | Utility.Utility.ATTRIBUTE_ERROR); } } return(result); }
/// <summary> /// Filters sources, returning sub-set having been modified since last /// change, as specified by <c>journalData</c>. /// </summary> /// <param name="filter">Filter callback to exclude filtered items</param> /// <returns>Filtered sources</returns> public IEnumerable <string> GetModifiedSources(Utility.Utility.EnumerationFilterDelegate filter) { // iterate over volumes foreach (var volumeData in m_volumeDataDict) { // prepare cache for includes (value = true) and excludes (value = false, will be populated // on-demand) var cache = new Dictionary <string, bool>(); foreach (var source in m_sources) { if (m_token.IsCancellationRequested) { break; } cache[source] = true; } // Check the simplified folders, and their parent folders against the exclusion filter. // This is needed because the filter may exclude "C:\A\", but this won't match the more // specific "C:\A\B\" in our list, even though it's meant to be excluded. // The reason why the filter doesn't exclude it is because during a regular (non-USN) full scan, // FilterHandler.EnumerateFilesAndFolders() works top-down, and won't even enumerate child // folders. // The sources are needed to stop evaluating parent folders above the specified source folders if (volumeData.Value.Folders != null) { foreach (var folder in FilterExcludedFolders(volumeData.Value.Folders, filter, cache).Where(m_snapshot.DirectoryExists)) { if (m_token.IsCancellationRequested) { break; } yield return(folder); } } // The simplified file list also needs to be checked against the exclusion filter, as it // may contain entries excluded due to attributes, but also because they are below excluded // folders, which themselves aren't in the folder list from step 1. // Note that the simplified file list may contain entries that have been deleted! They need to // be kept in the list (unless excluded by the filter) in order for the backup handler to record their // deletion. if (volumeData.Value.Files != null) { foreach (var files in FilterExcludedFiles(volumeData.Value.Files, filter, cache).Where(m_snapshot.FileExists)) { if (m_token.IsCancellationRequested) { break; } yield return(files); } } } }
/// <summary> /// Tests if specified folder, or any of its ancestors, is excluded by the filter /// </summary> /// <param name="folder">Folder to test</param> /// <param name="filter">Filter</param> /// <param name="cache">Cache of excluded folders (optional)</param> /// <returns>True if excluded, false otherwise</returns> private bool IsFolderOrAncestorsExcluded(string folder, Utility.Utility.EnumerationFilterDelegate filter, IDictionary <string, bool> cache) { List <string> parents = null; while (folder != null) { if (m_token.IsCancellationRequested) { break; } // first check cache if (cache.TryGetValue(folder, out var include)) { if (include) { return(false); } break; // hit! } // remember folder for cache if (parents == null) { parents = new List <string>(); // create on-demand } parents.Add(folder); var attr = m_snapshot.DirectoryExists(folder) ? m_snapshot.GetAttributes(folder) : FileAttributes.Directory; if (!filter(folder, folder, attr)) { break; // excluded } folder = Utility.Utility.GetParent(folder, true); } if (folder != null) { // update cache parents?.ForEach(p => cache[p] = false); } return(folder != null); }
/// <summary> /// Enumerates all files and folders in the shadow copy /// </summary> /// <param name="sources">Source paths to enumerate</param> /// <param name="callback">The callback to invoke with each found path</param> /// <param name="errorCallback">The callback used to report errors</param> public IEnumerable <string> EnumerateFilesAndFolders(IEnumerable <string> sources, Utility.Utility.EnumerationFilterDelegate callback, Utility.Utility.ReportAccessError errorCallback) { return(sources.SelectMany(s => Utility.Utility.EnumerateFileSystemEntries(s, callback, ListFolders, ListFiles, GetAttributes, errorCallback))); }
/// <summary> /// Enumerates all files and folders in the shadow copy /// </summary> /// <param name="sources">Source paths to enumerate</param> /// <param name="callback">The callback to invoke with each found path</param> /// <param name="errorCallback">The callback used to report errors</param> public IEnumerable <string> EnumerateFilesAndFolders(IEnumerable <string> sources, Utility.Utility.EnumerationFilterDelegate callback, Utility.Utility.ReportAccessError errorCallback) { // Add trailing slashes to folders var sanitizedSources = sources.Select(x => DirectoryExists(x) ? Utility.Utility.AppendDirSeparator(x) : x).ToList(); return(sanitizedSources.SelectMany( s => Utility.Utility.EnumerateFileSystemEntries(s, callback, ListFolders, ListFiles, GetAttributes, errorCallback) )); }
/// <summary> /// Enumerates all files and folders in the snapshot, restricted to sources /// </summary> /// <param name="sources">Sources to enumerate</param> /// <param name="callback">The callback to invoke with each found path</param> /// <param name="errorCallback">The callback used to report errors</param> public override IEnumerable <string> EnumerateFilesAndFolders(IEnumerable <string> sources, Utility.Utility.EnumerationFilterDelegate callback, Utility.Utility.ReportAccessError errorCallback) { // For Windows, ensure we don't store paths with UNC prefix return(base.EnumerateFilesAndFolders(sources.Select(SystemIOWindows.StripUNCPrefix), callback, errorCallback)); }
/// <summary> /// Enumerates all files and folders in the snapshot, restricted to sources /// </summary> /// <param name="sources">Sources to enumerate</param> /// <param name="callback">The callback to invoke with each found path</param> /// <param name="errorCallback">The callback used to report errors</param> public override IEnumerable <string> EnumerateFilesAndFolders(IEnumerable <string> sources, Utility.Utility.EnumerationFilterDelegate callback, Utility.Utility.ReportAccessError errorCallback) { // For Windows, ensure we don't store paths with extended device path prefixes (i.e., @"\\?\" or @"\\?\UNC\") return(base.EnumerateFilesAndFolders(sources.Select(SystemIOWindows.RemoveExtendedDevicePathPrefix), callback, errorCallback)); }