/// <summary> /// Create instance of USN journal service /// </summary> /// <param name="sources"></param> /// <param name="snapshot"></param> /// <param name="filter"></param> /// <param name="lastfilesetid"></param> /// <returns></returns> private UsnJournalService GetJournalService(IEnumerable <string> sources, ISnapshotService snapshot, IFilter filter, long lastfilesetid) { if (m_options.UsnStrategy == Options.OptimizationStrategy.Off) { return(null); } var journalData = m_database.GetChangeJournalData(lastfilesetid); var service = new UsnJournalService(sources, snapshot, filter, m_options.FileAttributeFilter, m_options.SkipFilesLargerThan, journalData, cancellationTokenSource.Token); foreach (var volumeData in service.VolumeDataList) { if (volumeData.IsFullScan) { if (volumeData.Exception == null || volumeData.Exception is UsnJournalSoftFailureException) { // soft fail Logging.Log.WriteInformationMessage(LOGTAG, "SkipUsnForVolume", "Performing full scan for volume \"{0}\": {1}", volumeData.Volume, volumeData.Exception?.Message); } else { if (m_options.UsnStrategy == Options.OptimizationStrategy.Auto) { Logging.Log.WriteInformationMessage(LOGTAG, "FailedToUseChangeJournal", "Failed to use change journal for volume \"{0}\": {1}", volumeData.Volume, volumeData.Exception.Message); } else if (m_options.UsnStrategy == Options.OptimizationStrategy.On) { Logging.Log.WriteWarningMessage(LOGTAG, "FailedToUseChangeJournal", volumeData.Exception, "Failed to use change journal for volume \"{0}\": {1}", volumeData.Volume, volumeData.Exception.Message); } else { throw volumeData.Exception; } } } } return(service); }
/// <summary> /// Performs the bulk of work by starting all relevant processes /// </summary> private static async Task RunMainOperation(IEnumerable <string> sources, Snapshots.ISnapshotService snapshot, UsnJournalService journalService, Backup.BackupDatabase database, Backup.BackupStatsCollector stats, Options options, IFilter sourcefilter, IFilter filter, BackupResults result, Common.ITaskReader taskreader, long lastfilesetid) { using (new Logging.Timer(LOGTAG, "BackupMainOperation", "BackupMainOperation")) { // Make sure the CompressionHints table is initialized, otherwise all workers will initialize it var tb = options.CompressionHints.Count; Task all; using (new ChannelScope()) { all = Task.WhenAll( new [] { Backup.DataBlockProcessor.Run(database, options, taskreader), Backup.FileBlockProcessor.Run(snapshot, options, database, stats, taskreader), Backup.StreamBlockSplitter.Run(options, database, taskreader), Backup.FileEnumerationProcess.Run(sources, snapshot, journalService, options.FileAttributeFilter, sourcefilter, filter, options.SymlinkPolicy, options.HardlinkPolicy, options.ExcludeEmptyFolders, options.IgnoreFilenames, options.ChangedFilelist, taskreader), Backup.FilePreFilterProcess.Run(snapshot, options, stats, database), Backup.MetadataPreProcess.Run(snapshot, options, database, lastfilesetid), Backup.SpillCollectorProcess.Run(options, database, taskreader), Backup.ProgressHandler.Run(result) } // Spawn additional block hashers .Union( Enumerable.Range(0, options.ConcurrencyBlockHashers - 1).Select(x => Backup.StreamBlockSplitter.Run(options, database, taskreader)) ) // Spawn additional compressors .Union( Enumerable.Range(0, options.ConcurrencyCompressors - 1).Select(x => Backup.DataBlockProcessor.Run(database, options, taskreader)) ) ); } await all.ConfigureAwait(false); if (options.ChangedFilelist != null && options.ChangedFilelist.Length >= 1) { await database.AppendFilesFromPreviousSetAsync(options.DeletedFilelist); } else if (journalService != null) { // append files from previous fileset, unless part of modifiedSources, which we've just scanned await database.AppendFilesFromPreviousSetWithPredicateAsync((path, fileSize) => { if (journalService.IsPathEnumerated(path)) { return(true); } if (fileSize >= 0) { stats.AddExaminedFile(fileSize); } return(false); }); // store journal data in database var data = journalService.VolumeDataList.Where(p => p.JournalData != null).Select(p => p.JournalData).ToList(); if (data.Any()) { // always record change journal data for current fileset (entry may be dropped later if nothing is uploaded) await database.CreateChangeJournalDataAsync(data); // update the previous fileset's change journal entry to resume at this point in case nothing was backed up await database.UpdateChangeJournalDataAsync(data, lastfilesetid); } } result.OperationProgressUpdater.UpdatefileCount(result.ExaminedFiles, result.SizeOfExaminedFiles, true); } }
public static Task Run(IEnumerable <string> sources, Snapshots.ISnapshotService snapshot, UsnJournalService journalService, FileAttributes fileAttributes, Duplicati.Library.Utility.IFilter sourcefilter, Duplicati.Library.Utility.IFilter emitfilter, Options.SymlinkStrategy symlinkPolicy, Options.HardlinkStrategy hardlinkPolicy, bool excludeemptyfolders, string[] ignorenames, string[] changedfilelist, ITaskReader taskreader) { return(AutomationExtensions.RunTask( new { Output = Backup.Channels.SourcePaths.ForWrite }, async self => { var hardlinkmap = new Dictionary <string, string>(); var mixinqueue = new Queue <string>(); Duplicati.Library.Utility.IFilter enumeratefilter = emitfilter; bool includes; bool excludes; Library.Utility.FilterExpression.AnalyzeFilters(emitfilter, out includes, out excludes); if (includes && !excludes) { enumeratefilter = Library.Utility.FilterExpression.Combine(emitfilter, new Duplicati.Library.Utility.FilterExpression("*" + System.IO.Path.DirectorySeparatorChar, true)); } // Simplify checking for an empty list if (ignorenames != null && ignorenames.Length == 0) { ignorenames = null; } // If we have a specific list, use that instead of enumerating the filesystem IEnumerable <string> worklist; if (changedfilelist != null && changedfilelist.Length > 0) { worklist = changedfilelist.Where(x => { var fa = FileAttributes.Normal; try { fa = snapshot.GetAttributes(x); } catch { } return AttributeFilter(x, fa, snapshot, sourcefilter, hardlinkPolicy, symlinkPolicy, hardlinkmap, fileAttributes, enumeratefilter, ignorenames, mixinqueue); }); } else { Library.Utility.Utility.EnumerationFilterDelegate attributeFilter = (root, path, attr) => AttributeFilter(path, attr, snapshot, sourcefilter, hardlinkPolicy, symlinkPolicy, hardlinkmap, fileAttributes, enumeratefilter, ignorenames, mixinqueue); if (journalService != null) { // filter sources using USN journal, to obtain a sub-set of files / folders that may have been modified sources = journalService.GetModifiedSources(attributeFilter); } worklist = snapshot.EnumerateFilesAndFolders(sources, attributeFilter, (rootpath, errorpath, ex) => { Logging.Log.WriteWarningMessage(FILTER_LOGTAG, "FileAccessError", ex, "Error reported while accessing file: {0}", errorpath); }); } var source = ExpandWorkList(worklist, mixinqueue, emitfilter, enumeratefilter); if (excludeemptyfolders) { source = ExcludeEmptyFolders(source); } // Process each path, and dequeue the mixins with symlinks as we go foreach (var s in source) { if (!await taskreader.ProgressAsync) { return; } await self.Output.WriteAsync(s); } })); }
public static async Task Run(IEnumerable <string> sources, Snapshots.ISnapshotService snapshot, UsnJournalService journalService, BackupResults result, Options options, IFilter sourcefilter, IFilter filter, Common.ITaskReader taskreader, System.Threading.CancellationToken token) { // Make sure we create the enumeration process in a separate scope, // but keep the log channel from the parent scope using (Logging.Log.StartIsolatingScope(true)) using (new IsolatedChannelScope()) { var enumeratorTask = Backup.FileEnumerationProcess.Run(sources, snapshot, journalService, options.FileAttributeFilter, sourcefilter, filter, options.SymlinkPolicy, options.HardlinkPolicy, options.ExcludeEmptyFolders, options.IgnoreFilenames, options.ChangedFilelist, taskreader, token); var counterTask = AutomationExtensions.RunTask(new { Input = Backup.Channels.SourcePaths.ForRead }, async self => { var count = 0L; var size = 0L; try { while (await taskreader.ProgressAsync && !token.IsCancellationRequested) { var path = await self.Input.ReadAsync(); count++; try { size += snapshot.GetFileSize(path); } catch { } result.OperationProgressUpdater.UpdatefileCount(count, size, false); } } finally { result.OperationProgressUpdater.UpdatefileCount(count, size, true); } }); await Task.WhenAll(enumeratorTask, counterTask); } }