private void OnFileFilter(FileInfo fileInfo, ref bool skip) { string err; GlobalScriptVars glob = new GlobalScriptVars(fileInfo, Stats.DtTmStarted); // Check if any of the global exclude rules tell us to skip processing on file // If any of the rules indicate skip, set flag and bail out for (int ndx = 0; ndx < sourcesToBackup[currentSrcNdx].GlobalExcludeRules.Count; ndx++) { string key = MakeCompiledCodeDictKey(sourcesToBackup[currentSrcNdx].ID, ndx); CompiledExclusionRule rule = globalExcludeDict[key]; if (rule.AppliesTo == RuleAppliesTo.Files) { bool doSkip = ScriptRunner.ExecuteCode(rule.CompiledCode, glob, out err); if (err == string.Empty) { if (doSkip) { Stats.FilesExcluded++; skip = doSkip; if (Stats.RunInDebugMode) { AddDebugEntryToLog($"Scripting rule {ndx} triggered skip of file: {fileInfo.FullName}"); } return; } } else { AddError($"File: {fileInfo.FullName} failed to process compiled script exclusion rule {ndx} on backup source {currentSrcNdx}"); skip = true; if (Stats.RunInDebugMode) { AddDebugEntryToLog($"Auto-skipped file {fileInfo.FullName} due to scripting compile/execution error on rule {ndx}"); } return; } } } }
private bool Validate(bool simMode, bool debugMode) { RunError = string.Empty; Stats = new BackupStats { JobID = this.JobID, RunInSimMode = simMode, RunInDebugMode = debugMode }; job = Settings.Jobs.FirstOrDefault <BackupJob>(j => j.ID == JobID); if (job == null) { var eligibleList = string.Join(", ", Settings.Jobs.Select(j => j.ID.ToString())); RunError = $"Job {JobID} is an invalid Backup Job ID. Try one of: {eligibleList}"; return(false); } // destination folder has JobID appended string destOutPath = Path.Combine(job.Destination, JobID.ToString()); // append trailing slash for small performance improvement over time so it's not constantly added for each filename grafted later if (destOutPath[destOutPath.Length - 1] != Path.DirectorySeparatorChar) { destOutPath += Path.DirectorySeparatorChar; } // set the destination root, then the dependent log and stats file names Stats.DestPathRoot = destOutPath; Stats.ErrLogFileName = GetDestRootFileNameLog(ErrorFilePrefix); Stats.StatsLogFileName = GetDestRootFileNameLog(StatsFilePrefix); Stats.DebugFileName = GetDestRootFileNameLog(DebugFilePrefix); // setup folder skips for delete detection. The paths must have a trailing slash so matching works correctly foldersToSkipForDeleteDetection.Clear(); foldersToSkipForDeleteDetection.Add(Path.Combine(Stats.DestPathRoot, HistoryFolderName) + Path.DirectorySeparatorChar); foldersToSkipForDeleteDetection.Add(Path.Combine(Stats.DestPathRoot, TrashFolderName) + Path.DirectorySeparatorChar); // build a list of known Bum files to skip (based on prefix convention) for delete detection prefixesToSkipForDeleteDetection.Clear(); prefixesToSkipForDeleteDetection.Add(Stats.DestPathRoot + ErrorFilePrefix + FileNameSegmentSep); prefixesToSkipForDeleteDetection.Add(Stats.DestPathRoot + StatsFilePrefix + FileNameSegmentSep); prefixesToSkipForDeleteDetection.Add(Stats.DestPathRoot + DebugFilePrefix + FileNameSegmentSep); // skip any Settings_* backup files prefixesToSkipForDeleteDetection.Add(Stats.DestPathRoot + BackupSettings.SettingsFilenameBase + FileNameSegmentSep); // make sure all folder structures exist including special folders for BUM FileUtils.CreateDestFolderStructure(Stats.DestPathRoot); if (Stats.RunInDebugMode) { AddDebugEntryToLog($"Creating root output folder (if not present): {Stats.DestPathRoot}"); } foreach (var folderName in foldersToSkipForDeleteDetection) { FileUtils.CreateDestFolderStructure(folderName); if (Stats.RunInDebugMode) { AddDebugEntryToLog($"Creating special output folder (if not present): {folderName}"); } } sourcesToBackup = new List <SourceDef>(); foreach (var id in job.SourceIDs) { SourceDef srcDef = Settings.SourceDefs.FirstOrDefault(s => s.ID == id); if (srcDef == null) { RunError = $"Job {JobID} references an invalid source ID {id} that is not found in the current settings"; return(false); } sourcesToBackup.Add(srcDef); // create dictionary lookup of "SouceID-Foldername" for each known exclusion folder foreach (var folder in srcDef.KnownFolderExcludes) { knownFolderExcludeDict.Add(MakeFolderExcludeDictKey(srcDef.ID, folder.Loc), folder); } for (int ndx = 0; ndx < srcDef.GlobalExcludeRules.Count; ndx++) { // global rule lookup id will use SourceID-RuleListIndex CompiledExclusionRule excRule = new CompiledExclusionRule(srcDef.GlobalExcludeRules[ndx]); if (excRule.HasErrors) { RunError = $"Job {JobID} and Backup Source ID {srcDef.ID} have compile errors in rule {ndx + 1}"; return(false); } globalExcludeDict.Add(MakeCompiledCodeDictKey(srcDef.ID, ndx), excRule); } } return(true); }
/// <summary>Custom folder filtering event. We use it for fast dictionary lookup of excluded names for each Source Backup ID</summary> /// <param name="folderInfo">DirectoryInfo representing the folder being processed</param> /// <param name="skip">set skip flag to inform DirSearch component to skip the current folder</param> /// <param name="skipChildFolders">set skipChildFolders flag to inform DirSearch component to skip the current folder's children</param> private void OnFolderFilter(DirectoryInfo folderInfo, ref bool skip, ref bool skipChildFolders) { string err; // check known folder excludes first string key = MakeFolderExcludeDictKey(sourcesToBackup[currentSrcNdx].ID, folderInfo.FullName); if (knownFolderExcludeDict.ContainsKey(key)) { skip = true; skipChildFolders = knownFolderExcludeDict[key].AllSubs; Stats.FoldersExcluded++; if (Stats.RunInDebugMode) { if (skipChildFolders) { AddDebugEntryToLog($"Fixed folder rule triggered skip of folder (and subfolders): {folderInfo.FullName}"); } else { AddDebugEntryToLog($"Fixed folder rule triggered skip of folder: {folderInfo.FullName}"); } } return; } // check folder-based rules GlobalScriptVars glob = new GlobalScriptVars(folderInfo, Stats.DtTmStarted); // Check if any of the global exclude rules tell us to skip processing on folder // If any of the rules indicate skip, set flag and bail out of folder and subfolders for (int ndx = 0; ndx < sourcesToBackup[currentSrcNdx].GlobalExcludeRules.Count; ndx++) { key = MakeCompiledCodeDictKey(sourcesToBackup[currentSrcNdx].ID, ndx); CompiledExclusionRule rule = globalExcludeDict[key]; if (rule.AppliesTo == RuleAppliesTo.FolderAndSubfolders || rule.AppliesTo == RuleAppliesTo.ParentFolderOnly) { bool doSkip = ScriptRunner.ExecuteCode(rule.CompiledCode, glob, out err); if (err == string.Empty) { if (doSkip) { skip = doSkip; skipChildFolders = (rule.AppliesTo == RuleAppliesTo.FolderAndSubfolders); Stats.FoldersExcluded++; if (Stats.RunInDebugMode) { if (skipChildFolders) { AddDebugEntryToLog($"Scripting rule {ndx} triggered skip of folder (and subfolders): {folderInfo.FullName}"); } else { AddDebugEntryToLog($"Scripting rule {ndx} triggered skip of folder: {folderInfo.FullName}"); } } return; } } else { AddError($"Folder: {folderInfo.FullName} failed to process compiled script exclusion rule {ndx} on backup source {currentSrcNdx}"); skip = true; if (Stats.RunInDebugMode) { AddDebugEntryToLog($"Auto-skipped folder {folderInfo.FullName} due to scripting compile/execution error on rule {ndx}"); } return; } } } }