From a9ca99996256e041ccd365fb617fef8d1f1ef4bf Mon Sep 17 00:00:00 2001 From: Kenneth Skovhede Date: Sat, 29 Jun 2013 12:17:52 +0200 Subject: Updated to allow files as input arguments to backup --- Duplicati/Library/Main/Controller.cs | 49 +++--- Duplicati/Library/Main/Operation/BackupHandler.cs | 8 +- Duplicati/Library/Main/Options.cs | 8 +- .../Library/Main/Strings/Controller.Designer.cs | 31 +--- Duplicati/Library/Main/Strings/Controller.resx | 11 +- Duplicati/Library/Main/Strings/Options.Designer.cs | 12 +- Duplicati/Library/Main/Strings/Options.resx | 8 +- .../Snapshots/Duplicati.Library.Snapshots.csproj | 2 + Duplicati/Library/Snapshots/LinuxSnapshot.cs | 6 +- Duplicati/Library/Snapshots/NoSnapshot.cs | 16 +- Duplicati/Library/Snapshots/NoSnapshotWindows.cs | 8 +- Duplicati/Library/Snapshots/WindowsSnapshot.cs | 2 +- Duplicati/Library/Utility/Utility.cs | 173 ++++++++++++--------- 13 files changed, 165 insertions(+), 169 deletions(-) (limited to 'Duplicati/Library') diff --git a/Duplicati/Library/Main/Controller.cs b/Duplicati/Library/Main/Controller.cs index 0b3e8bda8..94504c221 100644 --- a/Duplicati/Library/Main/Controller.cs +++ b/Duplicati/Library/Main/Controller.cs @@ -65,48 +65,59 @@ namespace Duplicati.Library.Main m_options = new Options(options); } - public Duplicati.Library.Interface.IBackupResults Backup(string[] sources, IFilter filter = null) + public Duplicati.Library.Interface.IBackupResults Backup(string[] inputsources, IFilter filter = null) { - return RunAction(new BackupResults(), ref sources, (result) => { + return RunAction(new BackupResults(), ref inputsources, (result) => { - if (sources == null || sources.Length == 0) + if (inputsources == null || inputsources.Length == 0) throw new Exception(Strings.Controller.NoSourceFoldersError); + var sources = new List(inputsources); + //Make sure they all have the same format and exist - for(int i = 0; i < sources.Length; i++) + for(int i = 0; i < sources.Count; i++) { - string fp; try { - fp = System.IO.Path.GetFullPath(sources[i]); + sources[i] = System.IO.Path.GetFullPath(sources[i]); } catch (Exception ex) { throw new ArgumentException(string.Format(Strings.Controller.InvalidPathError, sources[i], ex.Message), ex); } - sources[i] = Library.Utility.Utility.AppendDirSeparator(fp); - - if (!System.IO.Directory.Exists(sources[i]) && !m_options.AllowMissingSourceFolders) - throw new System.IO.IOException(String.Format(Strings.Controller.SourceFolderIsMissingError, sources[i])); + var fi = new System.IO.FileInfo(sources[i]); + var di = new System.IO.DirectoryInfo(sources[i]); + if (!(fi.Exists || di.Exists) && !m_options.AllowMissingSource) + throw new System.IO.IOException(String.Format(Strings.Controller.SourceIsMissingError, sources[i])); + + if (!fi.Exists) + sources[i] = Library.Utility.Utility.AppendDirSeparator(sources[i]); } //Sanity check for duplicate folders and multiple inclusions of the same folder - - //We could automatically fix this by excluding multiple copies of the same folder - // and remove the longest path of matching subfolders, - // but really the user should clean up the input - for(int i = 0; i < sources.Length - 1; i++) + for(int i = 0; i < sources.Count - 1; i++) { - for(int j = i + 1; j < sources.Length; j++) + for(int j = i + 1; j < sources.Count; j++) if (sources[i].Equals(sources[j], Library.Utility.Utility.IsFSCaseSensitive ? StringComparison.CurrentCulture : StringComparison.CurrentCultureIgnoreCase)) - throw new Exception(string.Format(Strings.Controller.SourceDirIsIncludedMultipleTimesError, sources[i])); + { + result.AddVerboseMessage("Removing duplicate source: {0}", sources[j]); + sources.RemoveAt(j); + j--; + } else if (sources[i].StartsWith(sources[j], Library.Utility.Utility.IsFSCaseSensitive ? StringComparison.CurrentCulture : StringComparison.CurrentCultureIgnoreCase)) - throw new Exception(string.Format(Strings.Controller.SourceDirsAreRelatedError, sources[i], sources[j])); + { + result.AddVerboseMessage("Removing source \"{0}\" because it is a subfolder of \"{1}\"", sources[i], sources[j]); + filter = Library.Utility.JoinedFilterExpression.Join(new FilterExpression(sources[i]), filter); + + sources.RemoveAt(i); + i--; + break; + } } using(var h = new Operation.BackupHandler(m_backend, m_options, result)) - h.Run(sources, filter); + h.Run(sources.ToArray(), filter); }); } diff --git a/Duplicati/Library/Main/Operation/BackupHandler.cs b/Duplicati/Library/Main/Operation/BackupHandler.cs index 8abd9d3a9..942c142a5 100644 --- a/Duplicati/Library/Main/Operation/BackupHandler.cs +++ b/Duplicati/Library/Main/Operation/BackupHandler.cs @@ -65,12 +65,12 @@ namespace Duplicati.Library.Main.Operation throw new Exception(string.Format(Strings.Foresthash.InvalidCryptoSystem, m_options.FileHashAlgorithm)); } - private static Snapshots.ISnapshotService GetSnapshot(string[] sourcefolders, Options options, ILogWriter log) + private static Snapshots.ISnapshotService GetSnapshot(string[] sources, Options options, ILogWriter log) { try { if (options.SnapShotStrategy != Options.OptimizationStrategy.Off) - return Duplicati.Library.Snapshots.SnapshotUtility.CreateSnapshot(sourcefolders, options.RawOptions); + return Duplicati.Library.Snapshots.SnapshotUtility.CreateSnapshot(sources, options.RawOptions); } catch (Exception ex) { @@ -83,9 +83,9 @@ namespace Duplicati.Library.Main.Operation } return Library.Utility.Utility.IsClientLinux ? - (Library.Snapshots.ISnapshotService)new Duplicati.Library.Snapshots.NoSnapshotLinux(sourcefolders, options.RawOptions) + (Library.Snapshots.ISnapshotService)new Duplicati.Library.Snapshots.NoSnapshotLinux(sources, options.RawOptions) : - (Library.Snapshots.ISnapshotService)new Duplicati.Library.Snapshots.NoSnapshotWindows(sourcefolders, options.RawOptions); + (Library.Snapshots.ISnapshotService)new Duplicati.Library.Snapshots.NoSnapshotWindows(sources, options.RawOptions); } diff --git a/Duplicati/Library/Main/Options.cs b/Duplicati/Library/Main/Options.cs index deb598956..2fb8bf789 100644 --- a/Duplicati/Library/Main/Options.cs +++ b/Duplicati/Library/Main/Options.cs @@ -187,7 +187,7 @@ namespace Duplicati.Library.Main "disable-autocreate-folder", "disable-filetime-check", "disable-time-tolerance", - "allow-missing-source-folders", + "allow-missing-source", "skip-files-larger-than", "upload-unchanged-backups", "list-verify-uploads", @@ -348,7 +348,7 @@ namespace Duplicati.Library.Main new CommandLineArgument("version", CommandLineArgument.ArgumentType.String, Strings.Options.VersionShort, Strings.Options.VersionLong, ""), new CommandLineArgument("all-versions", CommandLineArgument.ArgumentType.Boolean, Strings.Options.AllversionsShort, Strings.Options.AllversionsLong, "false"), new CommandLineArgument("disable-autocreate-folder", CommandLineArgument.ArgumentType.Boolean, Strings.Options.DisableautocreatefolderShort, Strings.Options.DisableautocreatefolderLong, "false"), - new CommandLineArgument("allow-missing-source-folders", CommandLineArgument.ArgumentType.Boolean, Strings.Options.AllowmissingsourcefoldersShort, Strings.Options.AllowmissingsourcefoldersLong, "false"), + new CommandLineArgument("allow-missing-source", CommandLineArgument.ArgumentType.Boolean, Strings.Options.AllowmissingsourceShort, Strings.Options.AllowmissingsourceLong, "false"), new CommandLineArgument("disable-filetime-check", CommandLineArgument.ArgumentType.Boolean, Strings.Options.DisablefiletimecheckShort, Strings.Options.DisablefiletimecheckLong, "false"), //new CommandLineArgument("disable-usn-diff-check", CommandLineArgument.ArgumentType.Boolean, Strings.Options.DisableusndiffcheckShort, Strings.Options.DisableusndiffcheckLong, "false"), @@ -1385,9 +1385,9 @@ namespace Duplicati.Library.Main /// /// Gets a flag indicating if compacting should not be done automatically /// - public bool AllowMissingSourceFolders + public bool AllowMissingSource { - get { return Library.Utility.Utility.ParseBoolOption(m_options, "allow-missing-source-folders"); } + get { return Library.Utility.Utility.ParseBoolOption(m_options, "allow-missing-source"); } } /// diff --git a/Duplicati/Library/Main/Strings/Controller.Designer.cs b/Duplicati/Library/Main/Strings/Controller.Designer.cs index 426d8ff6a..e098fc842 100644 --- a/Duplicati/Library/Main/Strings/Controller.Designer.cs +++ b/Duplicati/Library/Main/Strings/Controller.Designer.cs @@ -555,39 +555,12 @@ namespace Duplicati.Library.Main.Strings { } } - /// - /// Looks up a localized string similar to The folder {0} is included multiple times. - /// - internal static string SourceDirIsIncludedMultipleTimesError { - get { - return ResourceManager.GetString("SourceDirIsIncludedMultipleTimesError", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The folder {1} is a subfolder of {0}. It is not allowed to specify the same folder multiple times.. - /// - internal static string SourceDirsAreRelatedError { - get { - return ResourceManager.GetString("SourceDirsAreRelatedError", resourceCulture); - } - } - /// /// Looks up a localized string similar to The source folder {0} does not exist, aborting backup. /// - internal static string SourceFolderIsMissingError { - get { - return ResourceManager.GetString("SourceFolderIsMissingError", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The folder {0} is no longer in the source folder set. It is not allowed to change source folders for a backup.. - /// - internal static string SourceFoldersHasChangedError { + internal static string SourceIsMissingError { get { - return ResourceManager.GetString("SourceFoldersHasChangedError", resourceCulture); + return ResourceManager.GetString("SourceIsMissingError", resourceCulture); } } diff --git a/Duplicati/Library/Main/Strings/Controller.resx b/Duplicati/Library/Main/Strings/Controller.resx index 4fd635b6e..d88fcec6b 100644 --- a/Duplicati/Library/Main/Strings/Controller.resx +++ b/Duplicati/Library/Main/Strings/Controller.resx @@ -195,18 +195,9 @@ The signature file {0} was skipped because the signature file was not found in the manifest - - The folder {0} is included multiple times - - - The folder {1} is a subfolder of {0}. It is not allowed to specify the same folder multiple times. - - + The source folder {0} does not exist, aborting backup - - The folder {0} is no longer in the source folder set. It is not allowed to change source folders for a backup. - Building filelist ... diff --git a/Duplicati/Library/Main/Strings/Options.Designer.cs b/Duplicati/Library/Main/Strings/Options.Designer.cs index 07a42db72..66daf87ae 100644 --- a/Duplicati/Library/Main/Strings/Options.Designer.cs +++ b/Duplicati/Library/Main/Strings/Options.Designer.cs @@ -61,20 +61,20 @@ namespace Duplicati.Library.Main.Strings { } /// - /// Looks up a localized string similar to Use this option to set the timespan in which backups are kept.. + /// Looks up a localized string similar to Use this option to continue even if some source entries are missing.. /// - internal static string AllowmissingsourcefoldersLong { + internal static string AllowmissingsourceLong { get { - return ResourceManager.GetString("AllowmissingsourcefoldersLong", resourceCulture); + return ResourceManager.GetString("AllowmissingsourceLong", resourceCulture); } } /// - /// Looks up a localized string similar to Keep all versions within a timespan. + /// Looks up a localized string similar to Ignore missing source elements. /// - internal static string AllowmissingsourcefoldersShort { + internal static string AllowmissingsourceShort { get { - return ResourceManager.GetString("AllowmissingsourcefoldersShort", resourceCulture); + return ResourceManager.GetString("AllowmissingsourceShort", resourceCulture); } } diff --git a/Duplicati/Library/Main/Strings/Options.resx b/Duplicati/Library/Main/Strings/Options.resx index 0cb01abb0..ecf2d0234 100644 --- a/Duplicati/Library/Main/Strings/Options.resx +++ b/Duplicati/Library/Main/Strings/Options.resx @@ -540,11 +540,11 @@ Use this option to set the timespan in which backups are kept. - - Keep all versions within a timespan + + Ignore missing source elements - - Use this option to set the timespan in which backups are kept. + + Use this option to continue even if some source entries are missing. The file {0} was downloaded and had size {1} but the size was expected to be {2} diff --git a/Duplicati/Library/Snapshots/Duplicati.Library.Snapshots.csproj b/Duplicati/Library/Snapshots/Duplicati.Library.Snapshots.csproj index 7c5465e97..86708b640 100644 --- a/Duplicati/Library/Snapshots/Duplicati.Library.Snapshots.csproj +++ b/Duplicati/Library/Snapshots/Duplicati.Library.Snapshots.csproj @@ -48,6 +48,8 @@ ..\..\..\thirdparty\UnixSupport\UnixSupport.dll + + diff --git a/Duplicati/Library/Snapshots/LinuxSnapshot.cs b/Duplicati/Library/Snapshots/LinuxSnapshot.cs index 6f967c3d6..037f22db6 100644 --- a/Duplicati/Library/Snapshots/LinuxSnapshot.cs +++ b/Duplicati/Library/Snapshots/LinuxSnapshot.cs @@ -68,7 +68,7 @@ namespace Duplicati.Library.Snapshots public SnapShot(string path) { m_name = string.Format("duplicati-{0}", Guid.NewGuid().ToString()); - m_realDir = Utility.Utility.AppendDirSeparator(path); + m_realDir = System.IO.Directory.Exists(path) ? Utility.Utility.AppendDirSeparator(path) : path; GetVolumeName(m_realDir); } @@ -261,7 +261,7 @@ namespace Duplicati.Library.Snapshots /// /// The list of folders to create snapshots for /// A set of commandline options - public LinuxSnapshot(string[] folders, Dictionary options) + public LinuxSnapshot(string[] sources, Dictionary options) { try { @@ -269,7 +269,7 @@ namespace Duplicati.Library.Snapshots //Make sure we do not create more snapshots than we have to Dictionary snaps = new Dictionary(); - foreach (string s in folders) + foreach (string s in sources) { SnapShot sn = new SnapShot(s); if (!snaps.ContainsKey(sn.DeviceName)) diff --git a/Duplicati/Library/Snapshots/NoSnapshot.cs b/Duplicati/Library/Snapshots/NoSnapshot.cs index 528bb5e37..e9ede2e4e 100644 --- a/Duplicati/Library/Snapshots/NoSnapshot.cs +++ b/Duplicati/Library/Snapshots/NoSnapshot.cs @@ -35,7 +35,7 @@ namespace Duplicati.Library.Snapshots /// /// The list of all folders in the snapshot /// - protected string[] m_sourcefolders; + protected string[] m_sources; /// /// A frequently used char-as-string @@ -46,8 +46,8 @@ namespace Duplicati.Library.Snapshots /// Constructs a new backup snapshot, using all the required disks /// /// The folders that are about to be backed up - public NoSnapshot(string[] folders) - : this(folders, new Dictionary()) + public NoSnapshot(string[] sources) + : this(sources, new Dictionary()) { } @@ -56,11 +56,11 @@ namespace Duplicati.Library.Snapshots /// /// The folders that are about to be backed up /// A set of system options - public NoSnapshot(string[] folders, Dictionary options) + public NoSnapshot(string[] sources, Dictionary options) { - m_sourcefolders = new string[folders.Length]; - for (int i = 0; i < m_sourcefolders.Length; i++) - m_sourcefolders[i] = Utility.Utility.AppendDirSeparator(folders[i]); + m_sources = new string[sources.Length]; + for (int i = 0; i < m_sources.Length; i++) + m_sources[i] = System.IO.Directory.Exists(sources[i]) ? Utility.Utility.AppendDirSeparator(sources[i]) : sources[i]; } #region Private Methods @@ -101,7 +101,7 @@ namespace Duplicati.Library.Snapshots /// The callback to invoke with each found path public IEnumerable EnumerateFilesAndFolders(Duplicati.Library.Utility.Utility.EnumerationFilterDelegate callback) { - return m_sourcefolders.SelectMany( + return m_sources.SelectMany( s => Utility.Utility.EnumerateFileSystemEntries(s, callback, this.ListFolders, this.ListFiles, this.GetAttributes) ); } diff --git a/Duplicati/Library/Snapshots/NoSnapshotWindows.cs b/Duplicati/Library/Snapshots/NoSnapshotWindows.cs index e6d935358..9635bfce1 100644 --- a/Duplicati/Library/Snapshots/NoSnapshotWindows.cs +++ b/Duplicati/Library/Snapshots/NoSnapshotWindows.cs @@ -29,13 +29,13 @@ namespace Duplicati.Library.Snapshots { private SystemIOWindows m_sysIO = new SystemIOWindows(); - public NoSnapshotWindows(string[] sourcefolders) - : base(sourcefolders) + public NoSnapshotWindows(string[] sources) + : base(sources) { } - public NoSnapshotWindows(string[] sourcefolders, Dictionary options) - : base(sourcefolders, options) + public NoSnapshotWindows(string[] sources, Dictionary options) + : base(sources, options) { } diff --git a/Duplicati/Library/Snapshots/WindowsSnapshot.cs b/Duplicati/Library/Snapshots/WindowsSnapshot.cs index f2d35742d..d37520aae 100644 --- a/Duplicati/Library/Snapshots/WindowsSnapshot.cs +++ b/Duplicati/Library/Snapshots/WindowsSnapshot.cs @@ -105,7 +105,7 @@ namespace Duplicati.Library.Snapshots m_sourcepaths = new string[sourcepaths.Length]; for(int i = 0; i < m_sourcepaths.Length; i++) - m_sourcepaths[i] = Utility.Utility.AppendDirSeparator(sourcepaths[i]); + m_sourcepaths[i] = System.IO.Directory.Exists(sourcepaths[i]) ? Utility.Utility.AppendDirSeparator(sourcepaths[i]) : sourcepaths[i]; try { diff --git a/Duplicati/Library/Utility/Utility.cs b/Duplicati/Library/Utility/Utility.cs index a48e2a262..eb678985c 100644 --- a/Duplicati/Library/Utility/Utility.cs +++ b/Duplicati/Library/Utility/Utility.cs @@ -215,7 +215,7 @@ namespace Duplicati.Library.Utility /// /// The path to return data from /// Attributes for the file or folder - public delegate System.IO.FileAttributes ExtractFileAttributes(string path); + public delegate System.IO.FileAttributes ExtractFileAttributes(string path); /// /// Returns a list of all files found in the given folder. /// The search is recursive. @@ -224,10 +224,10 @@ namespace Duplicati.Library.Utility /// An optional filter to apply to the filenames /// The function to call with the filenames /// A list of the full filenames - public static IEnumerable EnumerateFileSystemEntries(string rootpath, EnumerationFilterDelegate callback) - { - return EnumerateFileSystemEntries(rootpath, callback, new FileSystemInteraction(System.IO.Directory.GetDirectories), new FileSystemInteraction(System.IO.Directory.GetFiles)); - } + public static IEnumerable EnumerateFileSystemEntries(string rootpath, EnumerationFilterDelegate callback) + { + return EnumerateFileSystemEntries(rootpath, callback, new FileSystemInteraction(System.IO.Directory.GetDirectories), new FileSystemInteraction(System.IO.Directory.GetFiles)); + } /// /// Returns a list of all files found in the given folder. /// The search is recursive. @@ -237,10 +237,10 @@ namespace Duplicati.Library.Utility /// A function to call that lists all folders in the supplied folder /// A function to call that lists all files in the supplied folder /// A list of the full filenames - public static IEnumerable EnumerateFileSystemEntries(string rootpath, EnumerationFilterDelegate callback, FileSystemInteraction folderList, FileSystemInteraction fileList) - { - return EnumerateFileSystemEntries(rootpath, callback, folderList, fileList, null); - } + public static IEnumerable EnumerateFileSystemEntries(string rootpath, EnumerationFilterDelegate callback, FileSystemInteraction folderList, FileSystemInteraction fileList) + { + return EnumerateFileSystemEntries(rootpath, callback, folderList, fileList, null); + } /// /// Returns a list of all files found in the given folder. /// The search is recursive. @@ -251,77 +251,96 @@ namespace Duplicati.Library.Utility /// A function to call that lists all files in the supplied folder /// A function to call that obtains the attributes for an element, set to null to avoid reading attributes /// A list of the full filenames - public static IEnumerable EnumerateFileSystemEntries(string rootpath, EnumerationFilterDelegate callback, FileSystemInteraction folderList, FileSystemInteraction fileList, ExtractFileAttributes attributeReader) - { - if (System.IO.Directory.Exists(rootpath)) - { - Queue lst = new Queue(); - lst.Enqueue(rootpath); + public static IEnumerable EnumerateFileSystemEntries(string rootpath, EnumerationFilterDelegate callback, FileSystemInteraction folderList, FileSystemInteraction fileList, ExtractFileAttributes attributeReader) + { + if (System.IO.Directory.Exists(rootpath)) + { + Queue lst = new Queue(); + lst.Enqueue(rootpath); - while (lst.Count > 0) - { - string f = AppendDirSeparator(lst.Dequeue()); - string fv = null; - try - { - System.IO.FileAttributes attr = attributeReader == null ? System.IO.FileAttributes.Directory : attributeReader(f); - if (!callback(rootpath, f, attr)) - continue; - - fv = f; - - foreach(string s in folderList(f)) - lst.Enqueue(s); - } - catch (System.Threading.ThreadAbortException) - { - throw; - } - catch (Exception) - { - callback(rootpath, f, ATTRIBUTE_ERROR | System.IO.FileAttributes.Directory); - } + while (lst.Count > 0) + { + string f = AppendDirSeparator(lst.Dequeue()); + string fv = null; + try + { + System.IO.FileAttributes attr = attributeReader == null ? System.IO.FileAttributes.Directory : attributeReader(f); + if (!callback(rootpath, f, attr)) + continue; + + fv = f; - if (fv != null) - yield return fv; + foreach(string s in folderList(f)) + lst.Enqueue(s); + } + catch (System.Threading.ThreadAbortException) + { + throw; + } + catch (Exception) + { + callback(rootpath, f, ATTRIBUTE_ERROR | System.IO.FileAttributes.Directory); + } + + if (fv != null) + yield return fv; + + string[] files = null; + if (fileList != null) + try + { + files = fileList(f); + } + catch (System.Threading.ThreadAbortException) + { + throw; + } + catch (Exception) + { + callback(rootpath, f, ATTRIBUTE_ERROR); + } - string[] files = null; - if (fileList != null) - try - { - files = fileList(f); - } - catch (System.Threading.ThreadAbortException) - { - throw; - } - catch (Exception) - { - callback(rootpath, f, ATTRIBUTE_ERROR); - } - - if (files != null) - foreach(var s in files) - { - try - { - System.IO.FileAttributes attr = attributeReader == null ? System.IO.FileAttributes.Normal : attributeReader(s); - if (!callback(rootpath, s, attr)) - continue; - } - catch (System.Threading.ThreadAbortException) - { - throw; - } - catch (Exception) - { - callback(rootpath, s, ATTRIBUTE_ERROR); - continue; - } - yield return s; - } - } - } + if (files != null) + foreach(var s in files) + { + try + { + System.IO.FileAttributes attr = attributeReader == null ? System.IO.FileAttributes.Normal : attributeReader(s); + if (!callback(rootpath, s, attr)) + continue; + } + catch (System.Threading.ThreadAbortException) + { + throw; + } + catch (Exception) + { + callback(rootpath, s, ATTRIBUTE_ERROR); + continue; + } + yield return s; + } + } + } + else if (System.IO.File.Exists(rootpath)) + { + try + { + System.IO.FileAttributes attr = attributeReader == null ? System.IO.FileAttributes.Normal : attributeReader(rootpath); + if (!callback(rootpath, rootpath, attr)) + yield break; + } + catch (System.Threading.ThreadAbortException) + { + throw; + } + catch (Exception) + { + callback(rootpath, rootpath, ATTRIBUTE_ERROR); + yield break; + } + yield return rootpath; + } } /// /// Calculates the size of files in a given folder -- cgit v1.2.3