diff --git a/Duplicati/Library/Interface/IGenericSourceModule.cs b/Duplicati/Library/Interface/IGenericSourceModule.cs index c0caa92ac..bc59d9058 100644 --- a/Duplicati/Library/Interface/IGenericSourceModule.cs +++ b/Duplicati/Library/Interface/IGenericSourceModule.cs @@ -19,54 +19,30 @@ #endregion using System; using System.Collections.Generic; -using System.Text; namespace Duplicati.Library.Interface { /// - /// An interface for a plugable generic module. + /// An interface for a plugable source module. /// An instance of a module is loaded prior to a backup or restore operation, - /// and can perform tasks relating to the general execution environment, as - /// well as modify the options used in Duplicati. - /// - /// The implementation must have a default constructor. - /// If the module is actually loaded, the Configure method is called. - /// All instances where the Configure method is called will be disposed, - /// if they implement the IDisposable interface as well. + /// and can perform tasks relating to the source plugins, as + /// well as modify backup source, the options and filters used in Duplicati. /// - public interface IGenericSourceModule + public interface IGenericSourceModule : IGenericModule { - ///// - ///// The module key, used to activate or deactivate the module on the commandline - ///// - //string Key { get; } - - ///// - ///// A localized string describing the module with a friendly name - ///// - //string DisplayName { get; } - - ///// - ///// A localized description of the module - ///// - //string Description { get; } - - ///// - ///// A boolean value that indicates if the module should always be loaded. - ///// If true, the user can choose to not load the module by entering the appropriate commandline option. - ///// If false, the user can choose to load the module by entering the appropriate commandline option. - ///// - //bool LoadAsDefault { get; } - - ///// - ///// Gets a list of supported commandline arguments - ///// - //IList SupportedCommands { get; } + /// + /// This method parse and alter backup source paths, apply and alter filters and returns changed or added options values. + /// + /// Backup source paths + /// Filters that are applied to backup paths (include, exclude) + /// A list of changed or added options values + Dictionary ParseSource(ref string[] paths, ref string filter); /// /// This method is the interception where the module can interact with the execution environment and modify the settings. /// /// A set of commandline options passed to Duplicati - void ParseSource(ref string[] paths, ref Dictionary commandlineOptions); + /// If module is going to backup anything, it returns true, otherwise false + bool ContainFiles(Dictionary commandlineOptions); } } diff --git a/Duplicati/Library/Main/Controller.cs b/Duplicati/Library/Main/Controller.cs index f978aede2..e10ecb92a 100644 --- a/Duplicati/Library/Main/Controller.cs +++ b/Duplicati/Library/Main/Controller.cs @@ -173,10 +173,17 @@ namespace Duplicati.Library.Main Library.UsageReporter.Reporter.Report("USE_BACKEND", new Library.Utility.Uri(m_backend).Scheme); Library.UsageReporter.Reporter.Report("USE_COMPRESSION", m_options.CompressionModule); Library.UsageReporter.Reporter.Report("USE_ENCRYPTION", m_options.EncryptionModule); - + return RunAction(new BackupResults(), ref inputsources, ref filter, (result) => { - if (inputsources == null || inputsources.Length == 0) + var bModulesFiles = false; + + foreach (var mx in m_options.LoadedModules) + if (mx.Key && mx.Value is Library.Interface.IGenericSourceModule) + if(!bModulesFiles) + bModulesFiles = ((Library.Interface.IGenericSourceModule)mx.Value).ContainFiles(m_options.RawOptions); + + if (!bModulesFiles && (inputsources == null || inputsources.Length == 0)) throw new Exception(Strings.Controller.NoSourceFoldersError); var sources = new List(inputsources); @@ -597,9 +604,8 @@ namespace Duplicati.Library.Main // Make the filter read-n-write able in the generic modules var pristinefilter = conopts["filter"] = string.Join(System.IO.Path.PathSeparator.ToString(), FilterExpression.Serialize(filter)); - var pristinepaths = paths; - foreach (KeyValuePair mx in m_options.LoadedModules) + foreach (var mx in m_options.LoadedModules) if (mx.Key) { if (mx.Value is Library.Interface.IConnectionModule) @@ -608,7 +614,12 @@ namespace Duplicati.Library.Main mx.Value.Configure(m_options.RawOptions); if (mx.Value is Library.Interface.IGenericSourceModule) - ((Library.Interface.IGenericSourceModule)mx.Value).ParseSource(ref paths, ref conopts); + { + var sourceoptions = ((Library.Interface.IGenericSourceModule)mx.Value).ParseSource(ref paths, ref pristinefilter); + + foreach (var sourceoption in sourceoptions) + m_options.RawOptions[sourceoption.Key] = sourceoption.Value; + } if (mx.Value is Library.Interface.IGenericCallbackModule) ((Library.Interface.IGenericCallbackModule)mx.Value).OnStart(result.MainOperation.ToString(), ref m_backend, ref paths); @@ -616,7 +627,7 @@ namespace Duplicati.Library.Main // If the filters were changed, read them back in if (pristinefilter != conopts["filter"]) - filter = FilterExpression.Deserialize(conopts["filter"].Split(new string[] {System.IO.Path.PathSeparator.ToString()}, StringSplitOptions.RemoveEmptyEntries)); + filter = FilterExpression.Deserialize(pristinefilter.Split(new string[] {System.IO.Path.PathSeparator.ToString()}, StringSplitOptions.RemoveEmptyEntries)); OperationRunning(true); diff --git a/Duplicati/Library/Main/Operation/BackupHandler.cs b/Duplicati/Library/Main/Operation/BackupHandler.cs index 3e2f33101..372d7ed5b 100644 --- a/Duplicati/Library/Main/Operation/BackupHandler.cs +++ b/Duplicati/Library/Main/Operation/BackupHandler.cs @@ -720,11 +720,9 @@ namespace Duplicati.Library.Main.Operation m_backendLogFlushTimer = DateTime.Now.Add(FLUSH_TIMESPAN); System.Threading.Thread parallelScanner = null; - try { - using(var backend = new BackendManager(m_backendurl, m_options, m_result.BackendWriter, m_database)) using(var filesetvolume = new FilesetVolumeWriter(m_options, m_database.OperationTimestamp)) { @@ -1012,7 +1010,6 @@ namespace Duplicati.Library.Main.Operation m_filehasher.Initialize(); - var offset = 0; var remaining = fs.Readblock(); diff --git a/Duplicati/Library/Main/Options.cs b/Duplicati/Library/Main/Options.cs index dffbfe506..3cac7f355 100644 --- a/Duplicati/Library/Main/Options.cs +++ b/Duplicati/Library/Main/Options.cs @@ -438,7 +438,6 @@ namespace Duplicati.Library.Main new CommandLineArgument("vss-exclude-writers", CommandLineArgument.ArgumentType.String, Strings.Options.VssexcludewritersShort, Strings.Options.VssexcludewritersLong), new CommandLineArgument("vss-use-mapping", CommandLineArgument.ArgumentType.Boolean, Strings.Options.VssusemappingShort, Strings.Options.VssusemappingLong, "false"), new CommandLineArgument("usn-policy", CommandLineArgument.ArgumentType.Enumeration, Strings.Options.UsnpolicyShort, Strings.Options.UsnpolicyLong, "off", null, Enum.GetNames(typeof(OptimizationStrategy))), - new CommandLineArgument("hyperv-backup-vm", CommandLineArgument.ArgumentType.String, Strings.Options.HypervbackupvmShort, Strings.Options.HypervbackupvmLong), new CommandLineArgument("encryption-module", CommandLineArgument.ArgumentType.String, Strings.Options.EncryptionmoduleShort, Strings.Options.EncryptionmoduleLong, "aes"), new CommandLineArgument("compression-module", CommandLineArgument.ArgumentType.String, Strings.Options.CompressionmoduleShort, Strings.Options.CompressionmoduleLong, "zip"), diff --git a/Duplicati/Library/Modules/Builtin/HyperVOptions.cs b/Duplicati/Library/Modules/Builtin/HyperVOptions.cs index 9aef4dda0..72e2dbd19 100644 --- a/Duplicati/Library/Modules/Builtin/HyperVOptions.cs +++ b/Duplicati/Library/Modules/Builtin/HyperVOptions.cs @@ -19,14 +19,13 @@ #endregion using System; using System.Collections.Generic; -using System.Text; using System.Linq; using System.Text.RegularExpressions; using Duplicati.Library.Snapshots; namespace Duplicati.Library.Modules.Builtin { - public class HyperVOptions : Interface.IGenericModule, Interface.IGenericSourceModule + public class HyperVOptions : Interface.IGenericSourceModule { private const string OPTION_SOURCE = "hyperv-backup-vm"; @@ -39,12 +38,12 @@ namespace Duplicati.Library.Modules.Builtin public string DisplayName { - get { return Strings.HttpOptions.DisplayName; } + get { return Strings.HyperVOptions.DisplayName; } } public string Description { - get { return Strings.HttpOptions.Description; } + get { return Strings.HyperVOptions.Description; } } public bool LoadAsDefault @@ -52,9 +51,9 @@ namespace Duplicati.Library.Modules.Builtin get { return true; } } - public IList SupportedCommands + public IList SupportedCommands { - get { return new List(); } + get { return new List(); } } public void Configure(IDictionary commandlineOptions) @@ -64,12 +63,13 @@ namespace Duplicati.Library.Modules.Builtin #endregion #region Implementation of IGenericSourceModule - public void ParseSource(ref string[] paths, ref Dictionary commandlineOptions) + public Dictionary ParseSource(ref string[] paths, ref string filter) { var hypervpathguidexp = @"%HYPERV:(\{){0,1}[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}(\}){0,1}%"; var hypervguidexp = @"(\{){0,1}[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}(\}){0,1}"; var hypervpathallexp = @"%HYPERV:*%"; var pathshyperv = new List(); + var ret = new Dictionary(); if (paths.Contains(hypervpathallexp, StringComparer.OrdinalIgnoreCase)) pathshyperv = new HyperVUtility().GetHyperVGuests().Select(x => string.Format("%HYPERV:{0}%", x.ID)).ToList(); @@ -78,20 +78,33 @@ namespace Duplicati.Library.Modules.Builtin paths = paths.Where(x => !x.Equals(hypervpathallexp, StringComparison.OrdinalIgnoreCase) && !Regex.IsMatch(x, hypervpathguidexp, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant)).ToArray(); - if (commandlineOptions.Keys.Contains("filter")) + if (!string.IsNullOrEmpty(filter)) { - var filters = commandlineOptions["filter"].Split(new string[] { System.IO.Path.PathSeparator.ToString() }, StringSplitOptions.RemoveEmptyEntries); + var filters = filter.Split(new string[] { System.IO.Path.PathSeparator.ToString() }, StringSplitOptions.RemoveEmptyEntries); + var filtersInclude = filters.Where(x => x.StartsWith("+") && Regex.IsMatch(x, hypervpathguidexp, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant)).Select(x => x.Substring(1)).ToList(); var filtersExclude = filters.Where(x => x.StartsWith("-") && Regex.IsMatch(x, hypervpathguidexp, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant)).Select(x => x.Substring(1)).ToList(); var remainingfilters = filters.Where(x => !Regex.IsMatch(x, hypervpathguidexp, RegexOptions.IgnoreCase)).ToArray(); - commandlineOptions["filter"] = string.Join(System.IO.Path.PathSeparator.ToString(), remainingfilters); + filter = string.Join(System.IO.Path.PathSeparator.ToString(), remainingfilters); pathshyperv = pathshyperv.Union(filtersInclude).Except(filtersExclude).ToList(); } pathshyperv = pathshyperv.Select(x => Regex.Match(x, hypervguidexp).Value).ToList(); - commandlineOptions[OPTION_SOURCE] = string.Join(System.IO.Path.PathSeparator.ToString(), pathshyperv); + ret[OPTION_SOURCE] = string.Join(System.IO.Path.PathSeparator.ToString(), pathshyperv); + return ret; + } + + public bool ContainFiles(Dictionary commandlineOptions) + { + if (commandlineOptions != null && !commandlineOptions.Keys.Contains(OPTION_SOURCE)) + return false; + + if (commandlineOptions[OPTION_SOURCE].Split(new string[] { System.IO.Path.PathSeparator.ToString() }, StringSplitOptions.RemoveEmptyEntries).Count() > 0) + return true; + else + return false; } #endregion diff --git a/Duplicati/Library/Modules/Builtin/Strings.cs b/Duplicati/Library/Modules/Builtin/Strings.cs index c5db3f410..1a1d16171 100644 --- a/Duplicati/Library/Modules/Builtin/Strings.cs +++ b/Duplicati/Library/Modules/Builtin/Strings.cs @@ -31,7 +31,11 @@ namespace Duplicati.Library.Modules.Builtin.Strings { public static string OauthurlLong { get { return LC.L(@"Duplicati uses an external server to support the OAuth authentication flow. If you have set up your own Duplicati OAuth server, you can supply the refresh url."); } } public static string SslversionsShort { get { return LC.L(@"Sets allowed SSL versions"); } } public static string SslversionsLong { get { return LC.L(@"This option changes the default SSL versions allowed. This is an advanced option and should only be used if you want to enhance security or work around an issue with a particular SSL protocol."); } } - } + } + internal static class HyperVOptions { + public static string Description { get { return LC.L(@"This module works internaly to parse source parameters to backup Hyper-V virtual machines"); } } + public static string DisplayName { get { return LC.L(@"Configure Hyper-V module"); } } + } internal static class RunScript { public static string Description { get { return LC.L(@"Executes a script before starting an operation, and again on completion"); } } public static string DisplayName { get { return LC.L(@"Run script"); } }