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"); } }