This commit is contained in:
JonMikelV
2018-01-04 22:59:00 -06:00
131 changed files with 3703 additions and 3242 deletions
@@ -18,6 +18,7 @@
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<TargetFrameworkProfile />
<UseMSBuildEngine>false</UseMSBuildEngine>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
@@ -68,7 +68,7 @@ namespace Duplicati.CommandLine.BackendTester
var p = Library.Utility.Utility.ExpandEnvironmentVariables(_args[0]);
if (System.IO.File.Exists(p))
_args = (from x in System.IO.File.ReadLines(p)
where !string.IsNullOrWhiteSpace(x) && !x.Trim().StartsWith("#")
where !string.IsNullOrWhiteSpace(x) && !x.Trim().StartsWith("#", StringComparison.Ordinal)
select x.Trim()
).ToArray();
}
@@ -121,7 +121,7 @@ namespace Duplicati.CommandLine.BackendTester
}
catch (Exception ex)
{
Console.WriteLine("Unittest failed: " + ex.ToString());
Console.WriteLine("Unittest failed: " + ex);
}
}
@@ -325,7 +325,7 @@ namespace Duplicati.CommandLine.BackendTester
}
if (e != null)
Console.WriteLine("failed\n*** Error: {0}", e.ToString());
Console.WriteLine("failed\n*** Error: {0}", e);
else
Console.WriteLine("done");
@@ -350,7 +350,7 @@ namespace Duplicati.CommandLine.BackendTester
try { backend.Delete(tx.remotefilename); }
catch (Exception ex)
{
Console.WriteLine("*** Failed to delete file {0}, message: {1}", tx.remotefilename, ex.ToString());
Console.WriteLine("*** Failed to delete file {0}, message: {1}", tx.remotefilename, ex);
}
curlist = backend.List();
@@ -397,11 +397,11 @@ namespace Duplicati.CommandLine.BackendTester
if (e != null)
{
Console.WriteLine("Failed to upload file {0}, error message: {1}, remote name: {2}", i, e.ToString(), remotefilename);
Console.WriteLine("Failed to upload file {0}, error message: {1}, remote name: {2}", i, e, remotefilename);
while (e.InnerException != null)
{
e = e.InnerException;
Console.WriteLine(string.Format(" Inner exception: {0}", e.ToString()));
Console.WriteLine(string.Format(" Inner exception: {0}", e));
}
}
else
@@ -12,6 +12,7 @@
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<TargetFrameworkProfile />
<UseMSBuildEngine>false</UseMSBuildEngine>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
+1 -1
View File
@@ -158,7 +158,7 @@ namespace Duplicati.CommandLine.BackendTool
{
Console.WriteLine("Command failed: " + ex.Message);
if (debugoutput || !(ex is UserInformationException))
Console.WriteLine(ex.ToString());
Console.WriteLine(ex);
return 100;
}
}
+5 -5
View File
@@ -260,7 +260,7 @@ namespace Duplicati.CommandLine
options["version"] = v.ToString();
}
}
else if (args[0].IndexOfAny(new char[] { '*', '?' }) < 0 && !args[0].StartsWith("["))
else if (args[0].IndexOfAny(new char[] { '*', '?' }) < 0 && !args[0].StartsWith("[", StringComparison.Ordinal))
{
try
{
@@ -277,7 +277,7 @@ namespace Duplicati.CommandLine
// Prefix all filenames with "*/" so we search all folders
for(var ix = 0; ix < args.Count; ix++)
if (args[ix].IndexOfAny(new char[] { '*', '?', System.IO.Path.DirectorySeparatorChar, System.IO.Path.AltDirectorySeparatorChar }) < 0 && !args[ix].StartsWith("["))
if (args[ix].IndexOfAny(new char[] { '*', '?', System.IO.Path.DirectorySeparatorChar, System.IO.Path.AltDirectorySeparatorChar }) < 0 && !args[ix].StartsWith("[", StringComparison.Ordinal))
args[ix] = "*" + System.IO.Path.DirectorySeparatorChar.ToString() + args[ix];
// Support for not adding the --auth-username if possible
@@ -360,7 +360,7 @@ namespace Duplicati.CommandLine
var f = res.Filesets.First();
outwriter.WriteLine("Listing contents {0} ({1}):", f.Version, f.Time);
foreach(var e in res.Files)
outwriter.WriteLine("{0} {1}", e.Path, e.Path.EndsWith(System.IO.Path.DirectorySeparatorChar.ToString()) ? "" : "(" + Library.Utility.Utility.FormatSizeString(e.Sizes.First()) + ")");
outwriter.WriteLine("{0} {1}", e.Path, e.Path.EndsWith(System.IO.Path.DirectorySeparatorChar.ToString(), StringComparison.Ordinal) ? "" : "(" + Library.Utility.Utility.FormatSizeString(e.Sizes.First()) + ")");
}
else
{
@@ -444,12 +444,12 @@ namespace Duplicati.CommandLine
// Prefix all filenames with "*/" so we search all folders
for (var ix = 0; ix < args.Count; ix++)
if (args[ix].IndexOfAny(new char[] { '*', '?', System.IO.Path.DirectorySeparatorChar, System.IO.Path.AltDirectorySeparatorChar }) < 0 && !args[ix].StartsWith("["))
if (args[ix].IndexOfAny(new char[] { '*', '?', System.IO.Path.DirectorySeparatorChar, System.IO.Path.AltDirectorySeparatorChar }) < 0 && !args[ix].StartsWith("[", StringComparison.Ordinal))
args[ix] = "*" + System.IO.Path.DirectorySeparatorChar.ToString() + args[ix];
// suffix all folders with "*" so we restore all contents in the folder
for (var ix = 0; ix < args.Count; ix++)
if (args[ix].IndexOfAny(new char[] { '*', '?' }) < 0 && !args[ix].StartsWith("[") && args[ix].EndsWith(System.IO.Path.DirectorySeparatorChar.ToString()))
if (args[ix].IndexOfAny(new char[] { '*', '?' }) < 0 && !args[ix].StartsWith("[", StringComparison.Ordinal) && args[ix].EndsWith(System.IO.Path.DirectorySeparatorChar.ToString(), StringComparison.Ordinal))
args[ix] += "*";
var output = new ConsoleOutput(outwriter, options);
@@ -19,6 +19,7 @@
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<TargetFrameworkProfile />
<UseMSBuildEngine>false</UseMSBuildEngine>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
+5 -5
View File
@@ -22,10 +22,10 @@ namespace Duplicati.CommandLine
StringBuilder sb = new StringBuilder();
foreach(var line in sr.ReadToEnd().Split(new string[] { "\r\n", "\n", "\r" }, StringSplitOptions.None))
{
if (line.Trim().StartsWith("#"))
if (line.Trim().StartsWith("#", StringComparison.Ordinal))
continue;
if (line.Trim().StartsWith(">"))
if (line.Trim().StartsWith(">", StringComparison.Ordinal))
{
if (sb.Length > 0)
{
@@ -410,13 +410,13 @@ namespace Duplicati.CommandLine
string c = s;
string leadingSpaces = "";
while (c.Length > 0 && c.StartsWith(" "))
while (c.Length > 0 && c.StartsWith(" ", StringComparison.Ordinal))
{
leadingSpaces += " ";
c = c.Remove(0, 1);
}
bool extraIndent = c.StartsWith("--");
bool extraIndent = c.StartsWith("--", StringComparison.Ordinal);
while (c.Length > 0)
{
@@ -424,7 +424,7 @@ namespace Duplicati.CommandLine
len -= leadingSpaces.Length;
if (len < c.Length)
{
int ix = c.LastIndexOf(" ", len);
int ix = c.LastIndexOf(" ", len, StringComparison.Ordinal);
if (ix > 0)
len = ix;
}
+1 -1
View File
@@ -270,7 +270,7 @@ namespace Duplicati.CommandLine
else if (!(ex is Library.Interface.CancelException))
{
errwriter.WriteLine();
errwriter.WriteLine(ex.ToString());
errwriter.WriteLine(ex);
}
else
{
@@ -122,7 +122,7 @@ namespace Duplicati.CommandLine.RecoveryTool
}
catch(Exception ex)
{
Console.WriteLine(" error: {0}", ex.ToString());
Console.WriteLine(" error: {0}", ex);
errors++;
}
@@ -11,6 +11,7 @@
<AssemblyOriginatorKeyFile>Duplicati.snk</AssemblyOriginatorKeyFile>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<UseMSBuildEngine>false</UseMSBuildEngine>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
+21 -20
View File
@@ -49,7 +49,7 @@ namespace Duplicati.CommandLine.RecoveryTool
ixfile = Path.GetFullPath(ixfile);
if (!File.Exists(ixfile))
{
using(File.Create(ixfile))
using (File.Create(ixfile))
{
}
}
@@ -67,7 +67,7 @@ namespace Duplicati.CommandLine.RecoveryTool
var errors = 0;
var totalblocks = 0L;
var files = 0;
foreach(var file in Directory.EnumerateFiles(folder))
foreach (var file in Directory.EnumerateFiles(folder))
{
Console.Write("{0}: {1}", i, file);
@@ -95,11 +95,12 @@ namespace Duplicati.CommandLine.RecoveryTool
var filekey = Path.GetFileName(file);
var blocks = 0;
using(var cp = Library.DynamicLoader.CompressionLoader.GetModule(p.CompressionModule, file, options))
using(var tf = new Library.Utility.TempFile())
using (var stream = new System.IO.FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read))
using (var cp = Library.DynamicLoader.CompressionLoader.GetModule(p.CompressionModule, stream, Library.Interface.ArchiveMode.Read, options))
using (var tf = new Library.Utility.TempFile())
{
using(var sw = new StreamWriter(tf))
foreach(var f in cp.ListFiles(null))
using (var sw = new StreamWriter(tf))
foreach (var f in cp.ListFiles(null))
{
sw.WriteLine("{0}, {1}", Library.Utility.Utility.Base64UrlToBase64Plain(f), filekey);
blocks++;
@@ -121,9 +122,9 @@ namespace Duplicati.CommandLine.RecoveryTool
Console.WriteLine(" done!");
}
}
catch(Exception ex)
catch (Exception ex)
{
Console.WriteLine(" error: {0}", ex.ToString());
Console.WriteLine(" error: {0}", ex);
errors++;
}
@@ -142,7 +143,7 @@ namespace Duplicati.CommandLine.RecoveryTool
try
{
// If the file can fit into memory, this is MUCH faster
using(var tfout = new Library.Utility.TempFile())
using (var tfout = new Library.Utility.TempFile())
{
var data = File.ReadAllLines(filein);
Array.Sort(data, StringComparer.Ordinal);
@@ -156,8 +157,8 @@ namespace Duplicati.CommandLine.RecoveryTool
{
}
using(var tfin = new Library.Utility.TempFile())
using(var tfout = new Library.Utility.TempFile())
using (var tfin = new Library.Utility.TempFile())
using (var tfout = new Library.Utility.TempFile())
{
long swaps;
@@ -167,15 +168,15 @@ namespace Duplicati.CommandLine.RecoveryTool
{
swaps = 0L;
using(var sw = new System.IO.StreamWriter(tfout))
using(var sr = new System.IO.StreamReader(tfin))
using (var sw = new System.IO.StreamWriter(tfout))
using (var sr = new System.IO.StreamReader(tfin))
{
var c1 = sr.ReadLine();
var c2 = sr.ReadLine();
while (c1 != null || c2 != null)
{
if (c1 != null && c1.StartsWith("a"))
if (c1 != null && c1.StartsWith("a", StringComparison.Ordinal))
Console.Write("");
var cmp = StringComparer.Ordinal.Compare(c1, c2);
@@ -198,7 +199,7 @@ namespace Duplicati.CommandLine.RecoveryTool
File.Copy(tfout, tfin, true);
} while(swaps > 0);
} while (swaps > 0);
File.Copy(tfout, fileout, true);
}
@@ -206,16 +207,16 @@ namespace Duplicati.CommandLine.RecoveryTool
private static void MergeFiles(string file1, string file2, string fileout)
{
using(var tf = new Library.Utility.TempFile())
using (var tf = new Library.Utility.TempFile())
{
using(var sw = new System.IO.StreamWriter(tf))
using(var sr1 = new System.IO.StreamReader(file1))
using(var sr2 = new System.IO.StreamReader(file2))
using (var sw = new System.IO.StreamWriter(tf))
using (var sr1 = new System.IO.StreamReader(file1))
using (var sr2 = new System.IO.StreamReader(file2))
{
var c1 = sr1.ReadLine();
var c2 = sr2.ReadLine();
while(c1 != null || c2 != null)
while (c1 != null || c2 != null)
{
var cmp = StringComparer.Ordinal.Compare(c1, c2);
+9 -8
View File
@@ -44,7 +44,7 @@ namespace Duplicati.CommandLine.RecoveryTool
if (args.Count == 2)
{
var times = ParseListFiles(folder);
foreach(var v in times.Zip(Enumerable.Range(0, times.Length), (a,b) => new KeyValuePair<int, DateTime>(b, a.Key)))
foreach (var v in times.Zip(Enumerable.Range(0, times.Length), (a, b) => new KeyValuePair<int, DateTime>(b, a.Key)))
Console.WriteLine("{0}: {1}", v.Key, v.Value.ToLocalTime());
}
else
@@ -55,7 +55,7 @@ namespace Duplicati.CommandLine.RecoveryTool
Library.Main.Volumes.VolumeReaderBase.UpdateOptionsFromManifest(p.CompressionModule, file, new Duplicati.Library.Main.Options(options));
foreach(var f in EnumerateFilesInDList(file, filter, options))
foreach (var f in EnumerateFilesInDList(file, filter, options))
Console.WriteLine("{0} ({1})", f.Path, Library.Utility.Utility.FormatSizeString(f.Size));
}
@@ -65,9 +65,10 @@ namespace Duplicati.CommandLine.RecoveryTool
public static IEnumerable<Duplicati.Library.Main.Volumes.IFileEntry> EnumerateFilesInDList(string file, Duplicati.Library.Utility.IFilter filter, Dictionary<string, string> options)
{
var p = Library.Main.Volumes.VolumeBase.ParseFilename(file);
using(var cm = Library.DynamicLoader.CompressionLoader.GetModule(p.CompressionModule, file, options))
using(var filesetreader = new Library.Main.Volumes.FilesetVolumeReader(cm, new Duplicati.Library.Main.Options(options)))
foreach(var f in filesetreader.Files)
using (var fs = new System.IO.FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read))
using (var cm = Library.DynamicLoader.CompressionLoader.GetModule(p.CompressionModule, fs, Library.Interface.ArchiveMode.Read, options))
using (var filesetreader = new Library.Main.Volumes.FilesetVolumeReader(cm, new Duplicati.Library.Main.Options(options)))
foreach (var f in filesetreader.Files)
{
if (f.Type != Duplicati.Library.Main.FilelistEntryType.File)
continue;
@@ -98,9 +99,9 @@ namespace Duplicati.CommandLine.RecoveryTool
var parsedtime = Library.Utility.Timeparser.ParseTimeInterval(time, DateTime.Now, true);
var el = (
from n in ParseListFiles(folder)
let diff = Math.Abs((n.Key - parsedtime).TotalSeconds)
orderby diff
select n).First();
let diff = Math.Abs((n.Key - parsedtime).TotalSeconds)
orderby diff
select n).First();
Console.WriteLine("Selected time {0}", el.Key);
@@ -97,7 +97,7 @@ namespace Duplicati.CommandLine.RecoveryTool
if (ex is Duplicati.Library.Interface.UserInformationException)
Console.WriteLine(ex.Message);
else
Console.WriteLine("Program crashed: {0}{1}", Environment.NewLine, ex.ToString());
Console.WriteLine("Program crashed: {0}{1}", Environment.NewLine, ex);
return 200;
}
}
@@ -125,7 +125,7 @@ namespace Duplicati.CommandLine.RecoveryTool
cargs.AddRange(
from c in fargs
where !string.IsNullOrWhiteSpace(c) && !c.StartsWith("#") && !c.StartsWith("!") && !c.StartsWith("REM ", StringComparison.OrdinalIgnoreCase)
where !string.IsNullOrWhiteSpace(c) && !c.StartsWith("#", StringComparison.Ordinal) && !c.StartsWith("!", StringComparison.Ordinal) && !c.StartsWith("REM ", StringComparison.OrdinalIgnoreCase)
select c
);
@@ -6,6 +6,7 @@ using Duplicati.Library.Main;
using Duplicati.Library.Main.Volumes;
using Duplicati.Library.Utility;
using Newtonsoft.Json.Linq;
using Duplicati.Library.Interface;
namespace Duplicati.CommandLine.RecoveryTool
{
@@ -130,7 +131,7 @@ namespace Duplicati.CommandLine.RecoveryTool
{
Console.WriteLine(" - cannot detect compression type");
continue;
}
}
if ((!reencrypt && File.Exists(localFileTarget)) || (reencrypt && File.Exists(localFileTarget + "." + localFileSourceEncryption)))
{
@@ -181,14 +182,15 @@ namespace Duplicati.CommandLine.RecoveryTool
File.Move(localFileSource, localFileSource + ".same");
localFileSource = localFileSource + ".same";
}
using (var cmOld = Library.DynamicLoader.CompressionLoader.GetModule(remoteFile.CompressionModule, localFileSource, options))
using (var cmNew = Library.DynamicLoader.CompressionLoader.GetModule(target_compr_module, localFileTarget, options))
using (var localFileSourceStream = new System.IO.FileStream(localFileSource, FileMode.Open, FileAccess.Read, FileShare.Read))
using (var cmOld = Library.DynamicLoader.CompressionLoader.GetModule(remoteFile.CompressionModule, localFileSourceStream, ArchiveMode.Read, options))
using (var localFileTargetStream = new FileStream(localFileTarget, FileMode.Create, FileAccess.Write, FileShare.Delete))
using (var cmNew = Library.DynamicLoader.CompressionLoader.GetModule(target_compr_module, localFileTargetStream, ArchiveMode.Write, options))
foreach (var cmfile in cmOld.ListFiles(""))
{
string cmfileNew = cmfile;
var cmFileVolume = VolumeBase.ParseFilename(cmfileNew);
if (remoteFile.FileType == RemoteVolumeType.Index && cmFileVolume != null && cmFileVolume.FileType == RemoteVolumeType.Blocks)
{
// Correct inner filename extension to target compression type
@@ -205,7 +207,7 @@ namespace Duplicati.CommandLine.RecoveryTool
JToken token = JObject.Parse(textJSON);
var fileInfoBlocks = new FileInfo(Path.Combine(targetfolder, cmfileNew.Replace("vol/", "")));
var filehasher = HashAlgorithmHelper.Create(m_Options.FileHashAlgorithm);
using (var fileStream = fileInfoBlocks.Open(FileMode.Open))
{
fileStream.Position = 0;
@@ -216,7 +218,7 @@ namespace Duplicati.CommandLine.RecoveryTool
token["volumesize"] = fileInfoBlocks.Length;
textJSON = token.ToString();
}
using (var sourceStream = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(textJSON)))
using (var cs = cmNew.CreateFile(cmfileNew, Library.Interface.CompressionHint.Compressible, cmOld.GetLastWriteTime(cmfile)))
Library.Utility.Utility.CopyStream(sourceStream, cs);
@@ -228,12 +230,12 @@ namespace Duplicati.CommandLine.RecoveryTool
Library.Utility.Utility.CopyStream(sourceStream, cs);
}
}
File.Delete(localFileSource);
destinationFileInfo = new FileInfo(localFileTarget);
destinationFileInfo.LastWriteTime = originLastWriteTime;
}
if (reencrypt && remoteFile.EncryptionModule != null)
{
Console.Write(" reencrypting ...");
@@ -247,7 +249,7 @@ namespace Duplicati.CommandLine.RecoveryTool
destinationFileInfo = new FileInfo(localFileTarget);
destinationFileInfo.LastWriteTime = originLastWriteTime;
}
if (reupload)
{
Console.Write(" reuploading ...");
@@ -261,7 +263,7 @@ namespace Duplicati.CommandLine.RecoveryTool
}
catch (Exception ex)
{
Console.WriteLine(" error: {0}", ex.ToString());
Console.WriteLine(" error: {0}", ex);
errors++;
}
}
+45 -31
View File
@@ -100,7 +100,7 @@ namespace Duplicati.CommandLine.RecoveryTool
var hashesprblock = blocksize / (blockhasher.HashSize / 8);
using(var mru = new CompressedFileMRUCache(options))
using (var mru = new CompressedFileMRUCache(options))
{
Console.WriteLine("Building lookup table for file hashes");
var lookup = new HashLookupHelper(ixfile, mru, (int)blocksize, blockhasher.HashSize / 8);
@@ -111,8 +111,8 @@ namespace Duplicati.CommandLine.RecoveryTool
if (!string.IsNullOrWhiteSpace(targetpath))
Console.WriteLine("Computing restore path");
foreach(var f in List.EnumerateFilesInDList(filelist, filter, options))
foreach (var f in List.EnumerateFilesInDList(filelist, filter, options))
{
if (largestprefix == null)
{
@@ -124,7 +124,7 @@ namespace Duplicati.CommandLine.RecoveryTool
var parts = f.Path.Split(new char[] { Path.DirectorySeparatorChar });
var ni = 0;
for(; ni < Math.Min(parts.Length, largestprefixparts.Length); ni++)
for (; ni < Math.Min(parts.Length, largestprefixparts.Length); ni++)
if (!Library.Utility.Utility.ClientFilenameStringComparer.Equals(parts[ni], largestprefixparts[ni]))
break;
@@ -155,19 +155,19 @@ namespace Duplicati.CommandLine.RecoveryTool
var i = 0L;
var errors = 0L;
foreach(var f in List.EnumerateFilesInDList(filelist, filter, options))
foreach (var f in List.EnumerateFilesInDList(filelist, filter, options))
{
try
{
var targetfile = MapToRestorePath(f.Path, largestprefix, targetpath);
if (!Directory.Exists(Path.GetDirectoryName(targetfile)))
Directory.CreateDirectory(Path.GetDirectoryName(targetfile));
Console.Write("{0}: {1} ({2})", i, targetfile, Library.Utility.Utility.FormatSizeString(f.Size));
using(var tf = new Library.Utility.TempFile())
using (var tf = new Library.Utility.TempFile())
{
using(var sw = File.OpenWrite(tf))
using (var sw = File.OpenWrite(tf))
{
if (f.BlocklistHashes == null)
{
@@ -176,7 +176,7 @@ namespace Duplicati.CommandLine.RecoveryTool
else
{
var blhi = 0L;
foreach(var blh in f.BlocklistHashes)
foreach (var blh in f.BlocklistHashes)
{
Console.Write(" {0}", blhi);
var blockhashoffset = blhi * hashesprblock * blocksize;
@@ -184,16 +184,16 @@ namespace Duplicati.CommandLine.RecoveryTool
try
{
var bi = 0;
foreach(var h in lookup.ReadBlocklistHashes(blh))
foreach (var h in lookup.ReadBlocklistHashes(blh))
{
try
{
sw.Position = blockhashoffset + (bi * blocksize);
lookup.WriteHash(sw, h);
}
catch(Exception ex)
catch (Exception ex)
{
Console.WriteLine("Failed to read hash: {0}{1}{2}", h, Environment.NewLine, ex.ToString());
Console.WriteLine("Failed to read hash: {0}{1}{2}", h, Environment.NewLine, ex);
}
bi++;
@@ -201,16 +201,16 @@ namespace Duplicati.CommandLine.RecoveryTool
}
catch (Exception ex)
{
Console.WriteLine("Failed to read Blocklist hash: {0}{1}{2}", blh, Environment.NewLine, ex.ToString());
Console.WriteLine("Failed to read Blocklist hash: {0}{1}{2}", blh, Environment.NewLine, ex);
}
blhi++;
}
}
}
string fh;
using(var fs = File.OpenRead(tf))
using (var fs = File.OpenRead(tf))
fh = Convert.ToBase64String(filehasher.ComputeHash(fs));
if (fh == f.Hash)
@@ -233,7 +233,7 @@ namespace Duplicati.CommandLine.RecoveryTool
}
catch (Exception ex)
{
Console.WriteLine(" error: {0}", ex.ToString());
Console.WriteLine(" error: {0}", ex);
errors++;
}
i++;
@@ -288,7 +288,7 @@ namespace Duplicati.CommandLine.RecoveryTool
var hashes = 0L;
string prev = null;
m_indexfile.Position = 0;
foreach(var n in AllFileLines())
foreach (var n in AllFileLines())
{
if (n.Key != prev)
{
@@ -308,7 +308,7 @@ namespace Duplicati.CommandLine.RecoveryTool
var prevoff = 0L;
var hc = 0L;
m_indexfile.Position = 0;
foreach(var n in AllFileLines())
foreach (var n in AllFileLines())
{
if (n.Key != prev)
{
@@ -331,9 +331,9 @@ namespace Duplicati.CommandLine.RecoveryTool
var max = m_indexfile.Read(m_linebuf, 0, m_linebuf.Length);
if (max == 0)
return null;
var lfi = 0;
for(int i = 0; i < max; i++)
for (int i = 0; i < max; i++)
if (m_linebuf[i] == m_newline[lfi])
{
if (lfi == m_newline.Length - 1)
@@ -352,12 +352,12 @@ namespace Duplicati.CommandLine.RecoveryTool
lfi = 0;
}
throw new Exception(string.Format("Unexpected long line starting at offset {0}, read {1} bytes without a newline", p, m_linebuf.Length));
throw new Exception(string.Format("Unexpected long line starting at offset {0}, read {1} bytes without a newline", p, m_linebuf.Length));
}
private IEnumerable<KeyValuePair<string, string>> AllFileLines()
{
while(true)
while (true)
{
var str = ReadNextLine();
if (str == null)
@@ -366,7 +366,7 @@ namespace Duplicati.CommandLine.RecoveryTool
if (str.Length == 0)
continue;
var ix = str.IndexOf(", ");
var ix = str.IndexOf(", ", StringComparison.Ordinal);
if (ix < 0)
Console.WriteLine("Failed to parse line starting at offset {0} in index file, string: {1}", m_indexfile.Position - str.Length - m_newline.Length, str);
yield return new KeyValuePair<string, string>(str.Substring(0, ix), str.Substring(ix + 2));
@@ -376,7 +376,7 @@ namespace Duplicati.CommandLine.RecoveryTool
public IEnumerable<string> ReadBlocklistHashes(string hash)
{
var bytes = ReadHash(hash);
for(var i = 0; i < bytes.Length; i += m_hashsize)
for (var i = 0; i < bytes.Length; i += m_hashsize)
yield return Convert.ToBase64String(bytes, i, m_hashsize);
}
@@ -392,10 +392,10 @@ namespace Duplicati.CommandLine.RecoveryTool
m_indexfile.Position = m_offsets[ix];
foreach(var v in AllFileLines())
foreach (var v in AllFileLines())
{
if (v.Key == hash)
using(var fs = m_cache.ReadBlock(v.Value, hash))
using (var fs = m_cache.ReadBlock(v.Value, hash))
{
var buf = new byte[m_blocksize];
var l = fs.Read(buf, 0, buf.Length);
@@ -403,8 +403,8 @@ namespace Duplicati.CommandLine.RecoveryTool
return buf;
}
if (StringComparer.Ordinal.Compare(hash, v.Key) < 0)
break;
}
@@ -430,6 +430,7 @@ namespace Duplicati.CommandLine.RecoveryTool
private class CompressedFileMRUCache : IDisposable
{
private Dictionary<string, Library.Interface.ICompression> m_lookup = new Dictionary<string, Duplicati.Library.Interface.ICompression>();
private Dictionary<string, Stream> m_streams = new Dictionary<string, Stream>();
private List<string> m_mru = new List<string>();
private Dictionary<string, string> m_options;
@@ -443,12 +444,18 @@ namespace Duplicati.CommandLine.RecoveryTool
public Stream ReadBlock(string filename, string hash)
{
Library.Interface.ICompression cf;
if (!m_lookup.TryGetValue(filename, out cf))
Stream stream;
if (!m_lookup.TryGetValue(filename, out cf) || !m_streams.TryGetValue(filename, out stream))
{
cf = Library.DynamicLoader.CompressionLoader.GetModule(Path.GetExtension(filename).Trim('.'), filename, m_options);
stream = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Delete);
cf = Library.DynamicLoader.CompressionLoader.GetModule(Path.GetExtension(filename).Trim('.'), stream, Library.Interface.ArchiveMode.Read, m_options);
if (cf == null)
{
stream.Dispose();
throw new Exception(string.Format("Unable to decompress {0}, no such compression module {1}", filename, Path.GetExtension(filename).Trim('.')));
}
m_lookup[filename] = cf;
m_streams[filename] = stream;
}
if (m_mru.Count > 0 && m_mru[0] != filename)
@@ -462,6 +469,8 @@ namespace Duplicati.CommandLine.RecoveryTool
var f = m_mru.Last();
m_lookup[f].Dispose();
m_lookup.Remove(f);
m_streams[f].Dispose();
m_streams.Remove(f);
m_mru.Remove(f);
}
@@ -472,11 +481,16 @@ namespace Duplicati.CommandLine.RecoveryTool
public void Dispose()
{
foreach(var v in m_lookup.Values)
foreach (var v in m_lookup.Values)
try { v.Dispose(); }
catch { }
foreach (var v in m_streams.Values)
try { v.Dispose(); }
catch { }
m_lookup = null;
m_streams = null;
m_mru = null;
}
+3 -1
View File
@@ -138,6 +138,8 @@ Usage: delete <storage-URL> [<options>]
Marks old data deleted and removes outdated dlist files. A backup is deleted when it is older than <keep-time> or when there are more newer versions than <keep-versions>. Data is considered old, when it is not required from any existing backup anymore.
--dry-run
Performs the operation, but does not write changes to the local database or the remote storage
--keep-time=<time>
Marks data outdated that is older than <time>.
--keep-versions=<int>
@@ -269,7 +271,7 @@ Usage: test-filters <source-path> [<options>]
Usage: affected <storage-URL> <remote-filename> [<remote-filenames>] [<options>]
Returns a report explaining what backup sets and files are affected by a remote file. You can use this option to see what source files are affected if one or more remote files are damaged or deleted.
Returns a report explaining what backup sets and files are affected by a remote file. You can use this option to see what source files are affected if one or more remote files are damaged or deleted. Notes that this command requires a local database to be present, if the database is not found automatically, you can set --dbpath to point to the database to use.
# =============================================================================
> duplicati.commandline.exe help vacuum
@@ -28,6 +28,7 @@
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<TargetFrameworkProfile />
<UseMSBuildEngine>false</UseMSBuildEngine>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|AnyCPU'">
<OutputPath>bin\Debug</OutputPath>
@@ -73,7 +73,7 @@ namespace Duplicati.GUI.TrayIcon
public HttpServerConnection(Uri server, string password, bool saltedpassword, bool dbPasswordSourceDatabase, Dictionary<string, string> options)
{
m_baseUri = server.ToString();
if (!m_baseUri.EndsWith("/"))
if (!m_baseUri.EndsWith("/", StringComparison.Ordinal))
m_baseUri += "/";
m_apiUri = m_baseUri + "api/v1";
@@ -225,7 +225,7 @@ namespace Duplicati.GUI.TrayIcon
var req = (System.Net.HttpWebRequest) System.Net.WebRequest.Create(m_baseUri + LOGIN_SCRIPT);
req.Method = "POST";
req.UserAgent = "Duplicati TrayIcon Monitor, v" +
System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString();
System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
req.Headers.Add(TRAYICONPASSWORDSOURCE_HEADER, m_TrayIconHeaderValue);
req.ContentType = "application/x-www-form-urlencoded";
@@ -252,7 +252,7 @@ namespace Duplicati.GUI.TrayIcon
(System.Net.HttpWebRequest) System.Net.WebRequest.Create(m_baseUri + LOGIN_SCRIPT);
req.Method = "POST";
req.UserAgent = "Duplicati TrayIcon Monitor, v" +
System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString();
System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
req.Headers.Add(TRAYICONPASSWORDSOURCE_HEADER, m_TrayIconHeaderValue);
req.ContentType = "application/x-www-form-urlencoded";
if (req.CookieContainer == null)
@@ -313,7 +313,7 @@ namespace Duplicati.GUI.TrayIcon
(System.Net.HttpWebRequest) System.Net.WebRequest.Create(m_baseUri + STATUS_WINDOW);
req.Method = "GET";
req.UserAgent = "Duplicati TrayIcon Monitor, v" +
System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString();
System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
req.Headers.Add(TRAYICONPASSWORDSOURCE_HEADER, m_TrayIconHeaderValue);
if (req.CookieContainer == null)
req.CookieContainer = new System.Net.CookieContainer();
@@ -404,7 +404,7 @@ namespace Duplicati.GUI.TrayIcon
if (m_xsrftoken != null)
req.Headers.Add(XSRF_HEADER, m_xsrftoken);
req.UserAgent = "Duplicati TrayIcon Monitor, v" +
System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString();
System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
req.Headers.Add(TRAYICONPASSWORDSOURCE_HEADER, m_TrayIconHeaderValue);
if (req.CookieContainer == null)
req.CookieContainer = new System.Net.CookieContainer();
@@ -250,15 +250,15 @@ namespace Duplicati.GUI.TrayIcon
}
catch (WebException ex)
{
System.Diagnostics.Trace.WriteLine("Request error: " + ex.ToString());
Console.WriteLine("Request error: " + ex.ToString());
System.Diagnostics.Trace.WriteLine("Request error: " + ex);
Console.WriteLine("Request error: " + ex);
reSpawn++;
}
catch (Exception ex)
{
System.Diagnostics.Trace.WriteLine("Unexpected error: " + ex.ToString());
Console.WriteLine("Unexpected error: " + ex.ToString());
System.Diagnostics.Trace.WriteLine("Unexpected error: " + ex);
Console.WriteLine("Unexpected error: " + ex);
return;
}
} while (reSpawn < 3);
@@ -177,8 +177,8 @@ namespace Duplicati.GUI.TrayIcon
m_toRumps = ch.AsWriteOnly();
WriteChannel(m_rumpsProcess.StandardInput, ch.AsReadOnly());
ReadChannel(m_rumpsProcess.StandardOutput);
ReadChannel(m_rumpsProcess.StandardError);
var standardOutputTask = ReadChannel(m_rumpsProcess.StandardOutput);
var standardErrorTask = ReadChannel(m_rumpsProcess.StandardError);
m_toRumps.WriteNoWait(JsonConvert.SerializeObject(new {Action = "background"}));
//m_toRumps.WriteNoWait(JsonConvert.SerializeObject(new {Action = "setappicon", Image = GetIcon(m_lastIcon)}));
@@ -243,7 +243,7 @@ namespace Duplicati.GUI.TrayIcon
menu.Callback();
}
}
else if (!line.StartsWith("info") && !string.IsNullOrWhiteSpace(line))
else if (!line.StartsWith("info", StringComparison.Ordinal) && !string.IsNullOrWhiteSpace(line))
{
Console.WriteLine("Unexpected message: {0}", line);
}
+153 -142
View File
@@ -20,6 +20,7 @@ using System.Linq;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using Duplicati.Library.Interface;
namespace Duplicati.Library.AutoUpdater
{
@@ -47,8 +48,8 @@ namespace Duplicati.Library.AutoUpdater
public static readonly string INSTALLDIR;
private static readonly string INSTALLED_BASE_DIR =
string.IsNullOrWhiteSpace(System.Environment.GetEnvironmentVariable(string.Format(BASEINSTALLDIR_ENVNAME_TEMPLATE, APPNAME)))
private static readonly string INSTALLED_BASE_DIR =
string.IsNullOrWhiteSpace(System.Environment.GetEnvironmentVariable(string.Format(BASEINSTALLDIR_ENVNAME_TEMPLATE, APPNAME)))
? System.IO.Path.GetDirectoryName(Duplicati.Library.Utility.Utility.getEntryAssembly().Location)
: Library.Utility.Utility.ExpandEnvironmentVariables(System.Environment.GetEnvironmentVariable(string.Format(BASEINSTALLDIR_ENVNAME_TEMPLATE, APPNAME)));
@@ -87,7 +88,7 @@ namespace Duplicati.Library.AutoUpdater
/// <summary>
/// Gets the last version found from an update
/// </summary>
public static UpdateInfo LastUpdateCheckVersion { get; private set; }
public static UpdateInfo LastUpdateCheckVersion { get; private set; }
static UpdaterManager()
{
@@ -118,7 +119,7 @@ namespace Duplicati.Library.AutoUpdater
var programfiles = System.Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);
// The user can override updates by having a local updates folder
var overrides = new List<string>(new string [] {
var overrides = new List<string>(new string[] {
System.IO.Path.Combine(InstalledBaseDir, "updates"),
});
@@ -131,7 +132,7 @@ namespace Duplicati.Library.AutoUpdater
{
if (Library.Utility.Utility.IsClientOSX)
overrides.Add(System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal), "Library", "Application Support", APPNAME, "updates"));
overrides.Add(System.IO.Path.Combine(System.Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), APPNAME, "updates"));
}
@@ -148,7 +149,7 @@ namespace Duplicati.Library.AutoUpdater
var attempts = new List<string>();
// We do not want to install anything in the basedir, if the application is installed in "ProgramFiles"
if (!string.IsNullOrWhiteSpace(programfiles) && !InstalledBaseDir.StartsWith(Library.Utility.Utility.AppendDirSeparator(programfiles)))
if (!string.IsNullOrWhiteSpace(programfiles) && !InstalledBaseDir.StartsWith(Library.Utility.Utility.AppendDirSeparator(programfiles), StringComparison.Ordinal))
attempts.Add(System.IO.Path.Combine(InstalledBaseDir, "updates"));
if (Library.Utility.Utility.IsClientOSX)
@@ -173,7 +174,7 @@ namespace Duplicati.Library.AutoUpdater
installdir = p;
break;
}
if (string.IsNullOrWhiteSpace(installdir))
foreach (var p in attempts)
if (!string.IsNullOrWhiteSpace(p) && TestDirectoryIsWriteable(p))
@@ -181,7 +182,7 @@ namespace Duplicati.Library.AutoUpdater
installdir = p;
break;
}
INSTALLDIR = installdir;
}
else
@@ -222,7 +223,7 @@ namespace Duplicati.Library.AutoUpdater
Displayname = string.IsNullOrWhiteSpace(Duplicati.License.VersionNumbers.TAG) ? "Current" : Duplicati.License.VersionNumbers.TAG,
Version = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString(),
ReleaseTime = new DateTime(0),
ReleaseType =
ReleaseType =
#if DEBUG
"Debug"
#else
@@ -257,13 +258,13 @@ namespace Duplicati.Library.AutoUpdater
{
var selfversion = TryParseVersion(SelfVersion.Version);
m_hasUpdateInstalled =
m_hasUpdateInstalled =
(from n in FindInstalledVersions()
let nversion = TryParseVersion(n.Value.Version)
let newerVersion = selfversion < nversion
where newerVersion && VerifyUnpackedFolder(n.Key, n.Value)
orderby nversion descending
select n)
let nversion = TryParseVersion(n.Value.Version)
let newerVersion = selfversion < nversion
where newerVersion && VerifyUnpackedFolder(n.Key, n.Value)
orderby nversion descending
select n)
.FirstOrDefault();
}
@@ -278,11 +279,11 @@ namespace Duplicati.Library.AutoUpdater
if (!System.IO.Directory.Exists(probe))
{
try
try
{
System.IO.Directory.CreateDirectory(probe);
if (probe != path)
System.IO.Directory.Delete(probe);
System.IO.Directory.Delete(probe);
return true;
}
catch
@@ -296,20 +297,20 @@ namespace Duplicati.Library.AutoUpdater
public static string InstallID
{
get
{
try { return System.IO.File.ReadAllText(System.IO.Path.Combine(INSTALLDIR, INSTALL_FILE)).Replace('\r', '\n').Split(new char[] { '\n' }).FirstOrDefault().Trim() ?? ""; }
{
try { return System.IO.File.ReadAllText(System.IO.Path.Combine(INSTALLDIR, INSTALL_FILE)).Replace('\r', '\n').Split(new char[] { '\n' }).FirstOrDefault().Trim() ?? ""; }
catch { }
return "";
}
}
public static UpdateInfo CheckForUpdate(ReleaseType channel = ReleaseType.Unknown)
{
if (channel == ReleaseType.Unknown)
channel = AutoUpdateSettings.DefaultUpdateChannel;
foreach(var rawurl in MANIFEST_URLS)
foreach (var rawurl in MANIFEST_URLS)
{
var url = rawurl;
@@ -332,17 +333,17 @@ namespace Duplicati.Library.AutoUpdater
try
{
using(var tmpfile = new Library.Utility.TempFile())
using (var tmpfile = new Library.Utility.TempFile())
{
System.Net.WebClient wc = new System.Net.WebClient();
wc.Headers.Add(System.Net.HttpRequestHeader.UserAgent, string.Format("{0} v{1}{2}", APPNAME, SelfVersion.Version, string.IsNullOrWhiteSpace(InstallID) ? "" : " -" + InstallID));
wc.Headers.Add("X-Install-ID", InstallID);
wc.DownloadFile(url, tmpfile);
using(var fs = System.IO.File.OpenRead(tmpfile))
using(var ss = new SignatureReadingStream(fs, SIGN_KEY))
using(var tr = new System.IO.StreamReader(ss))
using(var jr = new Newtonsoft.Json.JsonTextReader(tr))
using (var fs = System.IO.File.OpenRead(tmpfile))
using (var ss = new SignatureReadingStream(fs, SIGN_KEY))
using (var tr = new System.IO.StreamReader(ss))
using (var jr = new Newtonsoft.Json.JsonTextReader(tr))
{
var update = new Newtonsoft.Json.JsonSerializer().Deserialize<UpdateInfo>(jr);
@@ -384,10 +385,10 @@ namespace Duplicati.Library.AutoUpdater
{
try
{
using(var fs = System.IO.File.OpenRead(manifest))
using(var ss = new SignatureReadingStream(fs, SIGN_KEY))
using(var tr = new System.IO.StreamReader(ss))
using(var jr = new Newtonsoft.Json.JsonTextReader(tr))
using (var fs = System.IO.File.OpenRead(manifest))
using (var ss = new SignatureReadingStream(fs, SIGN_KEY))
using (var tr = new System.IO.StreamReader(ss))
using (var jr = new Newtonsoft.Json.JsonTextReader(tr))
return new Newtonsoft.Json.JsonSerializer().Deserialize<UpdateInfo>(jr);
}
catch (Exception ex)
@@ -404,7 +405,7 @@ namespace Duplicati.Library.AutoUpdater
{
var res = new List<KeyValuePair<string, UpdateInfo>>();
if (INSTALLDIR != null)
foreach(var folder in System.IO.Directory.GetDirectories(INSTALLDIR))
foreach (var folder in System.IO.Directory.GetDirectories(INSTALLDIR))
{
var r = ReadInstalledManifest(folder);
if (r != null)
@@ -429,20 +430,20 @@ namespace Duplicati.Library.AutoUpdater
var packagepath = new Library.Utility.Uri(updates[0]).Path;
var packagename = packagepath.Split('/').Last();
foreach(var alt_url in AutoUpdateSettings.URLs.Reverse())
foreach (var alt_url in AutoUpdateSettings.URLs.Reverse())
{
var alt_uri = new Library.Utility.Uri(alt_url);
var path_components = alt_uri.Path.Split('/');
var path = string.Join("/", path_components.Take(path_components.Count() - 1).Union(new string[] { packagename}));
var path = string.Join("/", path_components.Take(path_components.Count() - 1).Union(new string[] { packagename }));
var new_path = alt_uri.SetPath(path);
updates.Insert(0, new_path.ToString());
}
}
using(var tempfile = new Library.Utility.TempFile())
using (var tempfile = new Library.Utility.TempFileStream())
{
foreach(var url in updates)
foreach (var url in updates)
{
try
{
@@ -455,36 +456,35 @@ namespace Duplicati.Library.AutoUpdater
wreq.Headers.Add("X-Install-ID", InstallID);
var areq = new Duplicati.Library.Utility.AsyncHttpRequest(wreq);
using(var resp = areq.GetResponse())
using(var rss = areq.GetResponseStream())
using(var pgs = new Duplicati.Library.Utility.ProgressReportingStream(rss, version.CompressedSize, cb))
using(var fs = System.IO.File.Open(tempfile, System.IO.FileMode.Create))
Duplicati.Library.Utility.Utility.CopyStream(pgs, fs);
using (var resp = areq.GetResponse())
using (var rss = areq.GetResponseStream())
using (var pgs = new Duplicati.Library.Utility.ProgressReportingStream(rss, version.CompressedSize, cb))
{
Duplicati.Library.Utility.Utility.CopyStream(pgs, tempfile);
}
var sha256 = System.Security.Cryptography.SHA256.Create();
var md5 = System.Security.Cryptography.MD5.Create();
var md5 = System.Security.Cryptography.MD5.Create();
using(var s = System.IO.File.OpenRead(tempfile))
{
if (s.Length != version.CompressedSize)
throw new Exception(string.Format("Invalid file size {0}, expected {1} for {2}", s.Length, version.CompressedSize, url));
var sha256hash = Convert.ToBase64String(sha256.ComputeHash(s));
if (sha256hash != version.SHA256)
throw new Exception(string.Format("Damaged or corrupted file, sha256 mismatch for {0}", url));
}
if (tempfile.Length != version.CompressedSize)
throw new Exception(string.Format("Invalid file size {0}, expected {1} for {2}", tempfile.Length, version.CompressedSize, url));
using(var s = System.IO.File.OpenRead(tempfile))
tempfile.Position = 0;
var sha256hash = Convert.ToBase64String(sha256.ComputeHash(tempfile));
if (sha256hash != version.SHA256)
throw new Exception(string.Format("Damaged or corrupted file, sha256 mismatch for {0}", url));
tempfile.Position = 0;
var md5hash = Convert.ToBase64String(md5.ComputeHash(tempfile));
if (md5hash != version.MD5)
throw new Exception(string.Format("Damaged or corrupted file, md5 mismatch for {0}", url));
tempfile.Position = 0;
using (var tempfolder = new Duplicati.Library.Utility.TempFolder())
using (ICompression zip = new Duplicati.Library.Compression.FileArchiveZip(tempfile, ArchiveMode.Read, new Dictionary<string, string>()))
{
var md5hash = Convert.ToBase64String(md5.ComputeHash(s));
if (md5hash != version.MD5)
throw new Exception(string.Format("Damaged or corrupted file, md5 mismatch for {0}", url));
}
using(var tempfolder = new Duplicati.Library.Utility.TempFolder())
using(var zip = new Duplicati.Library.Compression.FileArchiveZip(tempfile, new Dictionary<string, string>()))
{
foreach(var file in zip.ListFilesWithSize(""))
foreach (var file in zip.ListFilesWithSize(""))
{
if (System.IO.Path.IsPathRooted(file.Key) || file.Key.Trim().StartsWith("..", StringComparison.OrdinalIgnoreCase))
throw new Exception(string.Format("Out-of-place file path detected: {0}", file.Key));
@@ -494,8 +494,8 @@ namespace Duplicati.Library.AutoUpdater
if (!System.IO.Directory.Exists(targetfolder))
System.IO.Directory.CreateDirectory(targetfolder);
using(var zs = zip.OpenRead(file.Key))
using(var fs = System.IO.File.Create(targetpath))
using (var zs = zip.OpenRead(file.Key))
using (var fs = System.IO.File.Create(targetpath))
zs.CopyTo(fs);
}
@@ -505,7 +505,7 @@ namespace Duplicati.Library.AutoUpdater
var targetfolder = System.IO.Path.Combine(INSTALLDIR, versionstring);
if (System.IO.Directory.Exists(targetfolder))
System.IO.Directory.Delete(targetfolder, true);
System.IO.Directory.CreateDirectory(targetfolder);
var tempfolderpath = Duplicati.Library.Utility.Utility.AppendDirSeparator(tempfolder);
@@ -514,14 +514,14 @@ namespace Duplicati.Library.AutoUpdater
// Would be nice, but does not work :(
//System.IO.Directory.Move(tempfolder, targetfolder);
foreach(var e in Duplicati.Library.Utility.Utility.EnumerateFileSystemEntries(tempfolder))
foreach (var e in Duplicati.Library.Utility.Utility.EnumerateFileSystemEntries(tempfolder))
{
var relpath = e.Substring(tempfolderlength);
if (string.IsNullOrWhiteSpace(relpath))
continue;
var fullpath = System.IO.Path.Combine(targetfolder, relpath);
if (relpath.EndsWith(System.IO.Path.DirectorySeparatorChar.ToString()))
if (relpath.EndsWith(System.IO.Path.DirectorySeparatorChar.ToString(), StringComparison.Ordinal))
System.IO.Directory.CreateDirectory(fullpath);
else
System.IO.File.Copy(e, fullpath);
@@ -530,16 +530,16 @@ namespace Duplicati.Library.AutoUpdater
// Verification will kick in when we list the installed updates
//VerifyUnpackedFolder(targetfolder, version);
System.IO.File.WriteAllText(System.IO.Path.Combine(INSTALLDIR, CURRENT_FILE), versionstring);
m_hasUpdateInstalled = null;
var obsolete = (from n in FindInstalledVersions()
where n.Value.Version != version.Version && n.Value.Version != SelfVersion.Version
let x = TryParseVersion(n.Value.Version)
orderby x descending
select n).Skip(1).ToArray();
where n.Value.Version != version.Version && n.Value.Version != SelfVersion.Version
let x = TryParseVersion(n.Value.Version)
orderby x descending
select n).Skip(1).ToArray();
foreach(var f in obsolete)
foreach (var f in obsolete)
try { System.IO.Directory.Delete(f.Key, true); }
catch { }
@@ -551,7 +551,7 @@ namespace Duplicati.Library.AutoUpdater
}
}
}
catch(Exception ex)
catch (Exception ex)
{
if (OnError != null)
OnError(ex);
@@ -572,11 +572,11 @@ namespace Duplicati.Library.AutoUpdater
var sha256 = System.Security.Cryptography.SHA256.Create();
var md5 = System.Security.Cryptography.MD5.Create();
using(var fs = System.IO.File.OpenRead(System.IO.Path.Combine(folder, UPDATE_MANIFEST_FILENAME)))
using (var fs = System.IO.File.OpenRead(System.IO.Path.Combine(folder, UPDATE_MANIFEST_FILENAME)))
{
using(var ss = new SignatureReadingStream(fs, SIGN_KEY))
using(var tr = new System.IO.StreamReader(ss))
using(var jr = new Newtonsoft.Json.JsonTextReader(tr))
using (var ss = new SignatureReadingStream(fs, SIGN_KEY))
using (var tr = new System.IO.StreamReader(ss))
using (var jr = new Newtonsoft.Json.JsonTextReader(tr))
update = new Newtonsoft.Json.JsonSerializer().Deserialize<UpdateInfo>(jr);
sha256.Initialize();
@@ -607,7 +607,7 @@ namespace Duplicati.Library.AutoUpdater
folder = Library.Utility.Utility.AppendDirSeparator(folder);
var baselen = folder.Length;
foreach(var file in Library.Utility.Utility.EnumerateFileSystemEntries(folder))
foreach (var file in Library.Utility.Utility.EnumerateFileSystemEntries(folder))
{
var relpath = file.Substring(baselen);
if (string.IsNullOrWhiteSpace(relpath))
@@ -620,7 +620,7 @@ namespace Duplicati.Library.AutoUpdater
if (!paths.TryGetValue(relpath, out fe))
{
var ignore = false;
foreach(var c in ignores)
foreach (var c in ignores)
if (ignore = relpath.StartsWith(c, Library.Utility.Utility.ClientFilenameStringComparision))
break;
@@ -638,7 +638,7 @@ namespace Duplicati.Library.AutoUpdater
sha256.Initialize();
md5.Initialize();
using(var fs = System.IO.File.OpenRead(file))
using (var fs = System.IO.File.OpenRead(file))
{
if (Convert.ToBase64String(sha256.ComputeHash(fs)) != fe.SHA256)
throw new Exception(string.Format("Invalid sha256 hash for file: {0}", file));
@@ -690,11 +690,11 @@ namespace Duplicati.Library.AutoUpdater
var manifestpath = manifest ?? System.IO.Path.Combine(inputfolder, UPDATE_MANIFEST_FILENAME);
using(var s = System.IO.File.OpenRead(manifestpath))
using(var sr = new System.IO.StreamReader(s))
using(var jr = new Newtonsoft.Json.JsonTextReader(sr))
using (var s = System.IO.File.OpenRead(manifestpath))
using (var sr = new System.IO.StreamReader(s))
using (var jr = new Newtonsoft.Json.JsonTextReader(sr))
remoteManifest = new Newtonsoft.Json.JsonSerializer().Deserialize<UpdateInfo>(jr);
if (remoteManifest.Files == null)
remoteManifest.Files = new FileEntry[0];
@@ -702,8 +702,8 @@ namespace Duplicati.Library.AutoUpdater
remoteManifest.ReleaseTime = DateTime.UtcNow;
var ignoreFiles = (from n in remoteManifest.Files
where n.Ignore
select n).ToArray();
where n.Ignore
select n).ToArray();
var ignoreMap = ignoreFiles.ToDictionary(k => k.Path, k => "", Duplicati.Library.Utility.Utility.ClientFilenameStringComparer);
@@ -724,60 +724,73 @@ namespace Duplicati.Library.AutoUpdater
var md5 = System.Security.Cryptography.MD5.Create();
var sha256 = System.Security.Cryptography.SHA256.Create();
Func<string, string> computeMD5 = (path) =>
Func<Stream, string> computeStreamMD5 = (stream) =>
{
md5.Initialize();
using(var fs = System.IO.File.OpenRead(path))
return Convert.ToBase64String(md5.ComputeHash(fs));
return Convert.ToBase64String(md5.ComputeHash(stream));
};
Func<Stream, string> computeStreamSHA256 = (stream) =>
{
sha256.Initialize();
return Convert.ToBase64String(sha256.ComputeHash(stream));
};
Func<string, string> computeMD5 = (path) =>
{
using (Stream fs = System.IO.File.OpenRead(path))
return computeStreamMD5(fs);
};
Func<string, string> computeSHA256 = (path) =>
{
sha256.Initialize();
using(var fs = System.IO.File.OpenRead(path))
return Convert.ToBase64String(sha256.ComputeHash(fs));
using (Stream fs = System.IO.File.OpenRead(path))
return computeStreamSHA256(fs);
};
// Build a zip
using (var archive_temp = new Duplicati.Library.Utility.TempFile())
using (var archive_temp_file = new Duplicati.Library.Utility.TempFile())
{
using (var zipfile = new Duplicati.Library.Compression.FileArchiveZip(archive_temp, new Dictionary<string, string>()))
using (var archive_temp = new Duplicati.Library.Utility.TempFileStream(archive_temp_file))
{
Func<string, string, bool> addToArchive = (path, relpath) =>
using (ICompression zipfile = new Duplicati.Library.Compression.FileArchiveZip(archive_temp, ArchiveMode.Write, new Dictionary<string, string>()))
{
if (ignoreMap.ContainsKey(relpath))
return false;
if (path.EndsWith(dirsep))
return true;
using (var source = System.IO.File.OpenRead(path))
using (var target = zipfile.CreateFile(relpath,
Duplicati.Library.Interface.CompressionHint.Compressible,
System.IO.File.GetLastAccessTimeUtc(path)))
Func<string, string, bool> addToArchive = (path, relpath) =>
{
source.CopyTo(target);
remoteManifest.UncompressedSize += source.Length;
}
if (ignoreMap.ContainsKey(relpath))
return false;
return true;
};
// Build the update manifest
localManifest.Files =
(from fse in Duplicati.Library.Utility.Utility.EnumerateFileSystemEntries(inputfolder)
let relpath = fse.Substring(baselen)
where addToArchive(fse, relpath)
select new FileEntry() {
Path = relpath,
LastWriteTime = System.IO.File.GetLastAccessTimeUtc(fse),
MD5 = fse.EndsWith(dirsep) ? null : computeMD5(fse),
SHA256 = fse.EndsWith(dirsep) ? null : computeSHA256(fse)
})
.Union(ignoreFiles).ToArray();
if (path.EndsWith(dirsep, StringComparison.Ordinal))
return true;
using (var source = System.IO.File.OpenRead(path))
using (var target = zipfile.CreateFile(relpath,
Duplicati.Library.Interface.CompressionHint.Compressible,
System.IO.File.GetLastAccessTimeUtc(path)))
{
source.CopyTo(target);
remoteManifest.UncompressedSize += source.Length;
}
return true;
};
// Build the update manifest
localManifest.Files =
(from fse in Duplicati.Library.Utility.Utility.EnumerateFileSystemEntries(inputfolder)
let relpath = fse.Substring(baselen)
where addToArchive(fse, relpath)
select new FileEntry()
{
Path = relpath,
LastWriteTime = System.IO.File.GetLastAccessTimeUtc(fse),
MD5 = fse.EndsWith(dirsep, StringComparison.Ordinal) ? null : computeMD5(fse),
SHA256 = fse.EndsWith(dirsep, StringComparison.Ordinal) ? null : computeSHA256(fse)
})
.Union(ignoreFiles).ToArray();
// Write a signed manifest with the files
// Write a signed manifest with the files
using (var ms = new System.IO.MemoryStream())
using (var sw = new System.IO.StreamWriter(ms))
{
@@ -788,26 +801,26 @@ namespace Duplicati.Library.AutoUpdater
{
SignatureReadingStream.CreateSignedStream(ms, ms2, key);
ms2.Position = 0;
using (var sigfile = zipfile.CreateFile(UPDATE_MANIFEST_FILENAME,
using (var sigfile = zipfile.CreateFile(UPDATE_MANIFEST_FILENAME,
Duplicati.Library.Interface.CompressionHint.Compressible,
DateTime.UtcNow))
ms2.CopyTo(sigfile);
}
}
}
archive_temp.Position = 0;
remoteManifest.CompressedSize = archive_temp.Length;
remoteManifest.MD5 = computeStreamMD5(archive_temp);
remoteManifest.SHA256 = computeStreamSHA256(archive_temp);
}
remoteManifest.CompressedSize = new System.IO.FileInfo(archive_temp).Length;
remoteManifest.MD5 = computeMD5(archive_temp);
remoteManifest.SHA256 = computeSHA256(archive_temp);
System.IO.File.Move(archive_temp, System.IO.Path.Combine(outputfolder, "package.zip"));
System.IO.File.Move(archive_temp_file, System.IO.Path.Combine(outputfolder, "package.zip"));
}
// Write a signed manifest for upload
using(var tf = new Duplicati.Library.Utility.TempFile())
using (var tf = new Duplicati.Library.Utility.TempFile())
{
using (var ms = new System.IO.MemoryStream())
using (var sw = new System.IO.StreamWriter(ms))
@@ -984,7 +997,7 @@ namespace Duplicati.Library.AutoUpdater
{
try
{
Console.WriteLine("Crash! {0}{1}", Environment.NewLine, tex.ToString());
Console.WriteLine("Crash! {0}{1}", Environment.NewLine, tex);
}
catch
{
@@ -997,7 +1010,7 @@ namespace Duplicati.Library.AutoUpdater
string.Format("{0}-crashlog.txt", AutoUpdateSettings.AppName)
);
System.IO.File.WriteAllText(report_file, tex.ToString());
System.IO.File.WriteAllText(report_file, tex.ToString());
}
catch
{
@@ -1070,9 +1083,8 @@ namespace Duplicati.Library.AutoUpdater
return r;
}
var args = Environment.CommandLine;
var app = Environment.GetCommandLineArgs().First();
args = args.Substring(app.Length);
var args = Library.Utility.Utility.WrapAsCommandLine(Environment.GetCommandLineArgs().Skip(1), false);
if (!Path.IsPathRooted(app))
app = Path.Combine(InstalledBaseDir, app);
@@ -1091,7 +1103,7 @@ namespace Duplicati.Library.AutoUpdater
RedirectStandardError = true,
RedirectStandardInput = true,
RedirectStandardOutput = true,
ErrorDialog = false,
ErrorDialog = false,
};
pi.EnvironmentVariables.Clear();
@@ -1201,7 +1213,7 @@ namespace Duplicati.Library.AutoUpdater
try { AppDomain.Unload(domain); }
catch (Exception ex)
{
{
Console.WriteLine("Appdomain unload error: {0}", ex);
}
@@ -1216,13 +1228,12 @@ namespace Duplicati.Library.AutoUpdater
try
{
var args = Environment.CommandLine;
var app = Environment.GetCommandLineArgs().First();
args = args.Substring(app.Length);
var args = Library.Utility.Utility.WrapAsCommandLine(Environment.GetCommandLineArgs().Skip(1), false);
if (!System.IO.Path.IsPathRooted(app))
app = System.IO.Path.Combine(InstalledBaseDir, app);
// Re-launch but give the OS a little time to fully unload all open handles, etc.
var si = new System.Diagnostics.ProcessStartInfo(app, args);
@@ -147,7 +147,7 @@ namespace Duplicati.Library.Backend.AlternativeFTP
_userInfo.Domain = "";
_url = u.SetScheme("ftp").SetQuery(null).SetCredentials(null, null).ToString();
if (!_url.EndsWith("/"))
if (!_url.EndsWith("/", StringComparison.Ordinal))
{
_url += "/";
}
@@ -215,7 +215,7 @@ namespace Duplicati.Library.Backend.AlternativeFTP
// Get the remote path
var url = new Uri(this._url);
remotePath = "/" + (url.AbsolutePath.EndsWith("/") ? url.AbsolutePath.Substring(0, url.AbsolutePath.Length - 1) : url.AbsolutePath);
remotePath = "/" + (url.AbsolutePath.EndsWith("/", StringComparison.Ordinal) ? url.AbsolutePath.Substring(0, url.AbsolutePath.Length - 1) : url.AbsolutePath);
if (!string.IsNullOrEmpty(filename))
{
@@ -354,7 +354,7 @@ namespace Duplicati.Library.Backend.AlternativeFTP
foreach (var fileEntry in fileEntries)
{
if (fileEntry.Name.Equals(remotename) || fileEntry.Name.EndsWith("/" + remotename) || fileEntry.Name.EndsWith("\\" + remotename))
if (fileEntry.Name.Equals(remotename) || fileEntry.Name.EndsWith("/" + remotename, StringComparison.Ordinal) || fileEntry.Name.EndsWith("\\" + remotename, StringComparison.Ordinal))
{
if (fileEntry.Size < 0 || streamLen < 0 || fileEntry.Size == streamLen)
{
@@ -522,7 +522,7 @@ namespace Duplicati.Library.Backend.AlternativeFTP
var url = new Uri(_url);
// Get the remote path
var remotePath = url.AbsolutePath.EndsWith("/") ? url.AbsolutePath.Substring(0, url.AbsolutePath.Length - 1) : url.AbsolutePath;
var remotePath = url.AbsolutePath.EndsWith("/", StringComparison.Ordinal) ? url.AbsolutePath.Substring(0, url.AbsolutePath.Length - 1) : url.AbsolutePath;
// Try to create the directory
client.CreateDirectory(remotePath, true);
@@ -561,7 +561,7 @@ namespace Duplicati.Library.Backend.AlternativeFTP
ftpClient.ValidateCertificate += HandleValidateCertificate;
// Get the remote path
var remotePath = uri.AbsolutePath.EndsWith("/") ? uri.AbsolutePath.Substring(0, uri.AbsolutePath.Length - 1) : uri.AbsolutePath;
var remotePath = uri.AbsolutePath.EndsWith("/", StringComparison.Ordinal) ? uri.AbsolutePath.Substring(0, uri.AbsolutePath.Length - 1) : uri.AbsolutePath;
ftpClient.SetWorkingDirectory(remotePath);
this.Client = ftpClient;
@@ -75,7 +75,7 @@ namespace Duplicati.Library.Backend.AmazonCloudDrive
var uri = new Utility.Uri(url);
m_path = uri.HostAndPath;
if (!m_path.EndsWith("/"))
if (!m_path.EndsWith("/", StringComparison.Ordinal))
m_path += "/";
string authid = null;
@@ -152,7 +152,7 @@ namespace Duplicati.Library.Backend.AmazonCloudDrive
{
if (m_endPointInfo == null)
RefreshMetadataAndContentUrl();
while (m_endPointInfo.ContentUrl.EndsWith("/"))
while (m_endPointInfo.ContentUrl.EndsWith("/", StringComparison.Ordinal))
m_endPointInfo.ContentUrl = m_endPointInfo.ContentUrl.Substring(0, m_endPointInfo.ContentUrl.Length - 1);
return m_endPointInfo.ContentUrl;
}
@@ -164,7 +164,7 @@ namespace Duplicati.Library.Backend.AmazonCloudDrive
{
if (m_endPointInfo == null)
RefreshMetadataAndContentUrl();
while (m_endPointInfo.MetadataUrl.EndsWith("/"))
while (m_endPointInfo.MetadataUrl.EndsWith("/", StringComparison.Ordinal))
m_endPointInfo.MetadataUrl = m_endPointInfo.MetadataUrl.Substring(0, m_endPointInfo.MetadataUrl.Length - 1);
return m_endPointInfo.MetadataUrl;
}
+3 -3
View File
@@ -57,11 +57,11 @@ namespace Duplicati.Library.Backend.Backblaze
m_bucketname = uri.Host;
m_prefix = "/" + uri.Path;
if (!m_prefix.EndsWith("/"))
if (!m_prefix.EndsWith("/", StringComparison.Ordinal))
m_prefix += "/";
// For B2 we do not use a leading slash
while(m_prefix.StartsWith("/"))
while(m_prefix.StartsWith("/", StringComparison.Ordinal))
m_prefix = m_prefix.Substring(1);
m_urlencodedprefix = string.Join("/", m_prefix.Split(new [] { '/' }).Select(x => Library.Utility.Uri.UrlPathEncode(x)));
@@ -323,7 +323,7 @@ namespace Duplicati.Library.Backend.Backblaze
foreach(var f in resp.Files)
{
if (!f.FileName.StartsWith(m_prefix))
if (!f.FileName.StartsWith(m_prefix, StringComparison.Ordinal))
continue;
var name = f.FileName.Substring(m_prefix.Length);
@@ -49,7 +49,7 @@ namespace Duplicati.Library.Backend.Backblaze
private string DropTrailingSlashes(string url)
{
while(url.EndsWith("/"))
while(url.EndsWith("/", StringComparison.Ordinal))
url = url.Substring(0, url.Length - 1);
return url;
}
+1 -1
View File
@@ -93,7 +93,7 @@ namespace Duplicati.Library.Backend.Box
var uri = new Utility.Uri(url);
m_path = uri.HostAndPath;
if (!m_path.EndsWith("/"))
if (!m_path.EndsWith("/", StringComparison.Ordinal))
m_path += "/";
string authid = null;
@@ -84,10 +84,10 @@ namespace Duplicati.Library.Backend
if (!string.IsNullOrEmpty(u.UserInfo))
{
if (u.UserInfo.IndexOf(":") >= 0)
if (u.UserInfo.IndexOf(":", StringComparison.Ordinal) >= 0)
{
m_username = u.UserInfo.Substring(0, u.UserInfo.IndexOf(":"));
m_password = u.UserInfo.Substring(u.UserInfo.IndexOf(":") + 1);
m_username = u.UserInfo.Substring(0, u.UserInfo.IndexOf(":", StringComparison.Ordinal));
m_password = u.UserInfo.Substring(u.UserInfo.IndexOf(":", StringComparison.Ordinal) + 1);
}
else
{
@@ -108,9 +108,9 @@ namespace Duplicati.Library.Backend
m_path = uri.HostAndPath;
}
if (m_path.EndsWith("/"))
if (m_path.EndsWith("/", StringComparison.Ordinal))
m_path = m_path.Substring(0, m_path.Length - 1);
if (!m_path.StartsWith("/"))
if (!m_path.StartsWith("/", StringComparison.Ordinal))
m_path = "/" + m_path;
if (!options.TryGetValue("cloudfiles-authentication-url", out m_authUrl))
@@ -416,7 +416,7 @@ namespace Duplicati.Library.Backend
HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create(m_storageUrl + UrlEncode(m_path + remotename) + query);
req.Headers.Add("X-Auth-Token", UrlEncode(m_authToken));
req.UserAgent = "Duplicati CloudFiles Backend v" + System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString();
req.UserAgent = "Duplicati CloudFiles Backend v" + System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
req.KeepAlive = false;
req.PreAuthenticate = true;
req.AllowWriteStreamBuffering = false;
+5 -5
View File
@@ -22,10 +22,10 @@ namespace Duplicati.Library.Backend
var uri = new Utility.Uri(url);
m_path = Library.Utility.Uri.UrlDecode(uri.HostAndPath);
if (m_path.Length != 0 && !m_path.StartsWith("/"))
if (m_path.Length != 0 && !m_path.StartsWith("/", StringComparison.Ordinal))
m_path = "/" + m_path;
if (m_path.EndsWith("/"))
if (m_path.EndsWith("/", StringComparison.Ordinal))
m_path = m_path.Substring(0, m_path.Length - 1);
if (options.ContainsKey(AUTHID_OPTION))
@@ -117,7 +117,7 @@ namespace Duplicati.Library.Backend
string path = String.Format("{0}/{1}", m_path, remotename);
dbx.Delete(path);
}
catch (DropboxException de)
catch (DropboxException)
{
// we can catch some events here and convert them to Duplicati exceptions
throw;
@@ -163,7 +163,7 @@ namespace Duplicati.Library.Backend
string path = string.Format("{0}/{1}", m_path, remotename);
dbx.UploadFile(path, stream);
}
catch (DropboxException de)
catch (DropboxException)
{
// we can catch some events here and convert them to Duplicati exceptions
throw;
@@ -177,7 +177,7 @@ namespace Duplicati.Library.Backend
string path = string.Format("{0}/{1}", m_path, remotename);
dbx.DownloadFile(path, stream);
}
catch (DropboxException de)
catch (DropboxException)
{
// we can catch some events here and convert them to Duplicati exceptions
throw;
+3 -3
View File
@@ -83,7 +83,7 @@ namespace Duplicati.Library.Backend
m_userInfo.Domain = "";
m_url = u.SetScheme("ftp").SetQuery(null).SetCredentials(null, null).ToString();
if (!m_url.EndsWith("/"))
if (!m_url.EndsWith("/", StringComparison.Ordinal))
m_url += "/";
m_useSSL = Utility.Utility.ParseBoolOption(options, "use-ssl");
@@ -278,7 +278,7 @@ namespace Duplicati.Library.Backend
{
IEnumerable<IFileEntry> files = List(remotename);
foreach(IFileEntry fe in files)
if (fe.Name.Equals(remotename) || fe.Name.EndsWith("/" + remotename) || fe.Name.EndsWith("\\" + remotename))
if (fe.Name.Equals(remotename) || fe.Name.EndsWith("/" + remotename, StringComparison.Ordinal) || fe.Name.EndsWith("\\" + remotename, StringComparison.Ordinal))
{
if (fe.Size < 0 || streamLen < 0 || fe.Size == streamLen)
return;
@@ -390,7 +390,7 @@ namespace Duplicati.Library.Backend
private System.Net.FtpWebRequest CreateRequest(string remotename, bool createFolder)
{
string url = m_url;
if (createFolder && url.EndsWith("/"))
if (createFolder && url.EndsWith("/", StringComparison.Ordinal))
url = url.Substring(0, url.Length - 1);
System.Net.FtpWebRequest req = (System.Net.FtpWebRequest)System.Net.FtpWebRequest.Create(url + remotename);
@@ -76,7 +76,7 @@ namespace Duplicati.Library.Backend
for (int i = 0; i < paths.Count; i++)
{
if (paths[i].StartsWith("*:"))
if (paths[i].StartsWith("*:", StringComparison.Ordinal))
{
string rpl_path = paths[i].Substring(1);
paths.RemoveAt(i);
@@ -304,7 +304,7 @@ namespace Duplicati.Library.Backend
//TODO: Can trick this with symlinks, where the symlink is on one mounted volume,
// and the actual storage is on another
foreach (System.IO.DriveInfo di in System.IO.DriveInfo.GetDrives())
if (path.StartsWith(Utility.Utility.AppendDirSeparator(di.Name)) && di.Name.Length > root.Length)
if (path.StartsWith(Utility.Utility.AppendDirSeparator(di.Name), StringComparison.Ordinal) && di.Name.Length > root.Length)
root = di.Name;
}
else
+3 -3
View File
@@ -95,13 +95,13 @@ namespace Duplicati.Library.Backend
//Strip it down from \\server\share\folder1\folder2\filename.extension to
// \\server\share
string minpath = path;
if (!minpath.StartsWith("\\\\"))
if (!minpath.StartsWith("\\\\", StringComparison.Ordinal))
return false;
int first = minpath.IndexOf("\\", 2);
int first = minpath.IndexOf("\\", 2, StringComparison.Ordinal);
if (first <= 0)
return false;
int next = minpath.IndexOf("\\", first + 1);
int next = minpath.IndexOf("\\", first + 1, StringComparison.Ordinal);
if (next >= 0)
minpath = minpath.Substring(0, next);
@@ -79,11 +79,11 @@ namespace Duplicati.Library.Backend.GoogleCloudStorage
m_bucket = uri.Host;
m_prefix = "/" + uri.Path;
if (!m_prefix.EndsWith("/"))
if (!m_prefix.EndsWith("/", StringComparison.Ordinal))
m_prefix += "/";
// For GCS we do not use a leading slash
if (m_prefix.StartsWith("/"))
if (m_prefix.StartsWith("/", StringComparison.Ordinal))
m_prefix = m_prefix.Substring(1);
string authid;
@@ -48,7 +48,7 @@ namespace Duplicati.Library.Backend.GoogleDrive
var uri = new Utility.Uri(url);
m_path = uri.HostAndPath;
if (!m_path.EndsWith("/"))
if (!m_path.EndsWith("/", StringComparison.Ordinal))
m_path += "/";
string authid = null;
@@ -115,7 +115,7 @@ namespace Duplicati.Library.Backend
m_path = u.HostAndPath; // Host and path of "jottacloud://folder/subfolder" is "folder/subfolder", so the actual folder path within the mount point.
if (string.IsNullOrEmpty(m_path)) // Require a folder. Actually it is possible to store files directly on the root level of the mount point, but that does not seem to be a good option.
throw new UserInformationException(Strings.Jottacloud.NoPathError);
if (!m_path.EndsWith("/"))
if (!m_path.EndsWith("/", StringComparison.Ordinal))
m_path += "/";
if (!string.IsNullOrEmpty(u.Username))
{
@@ -300,7 +300,7 @@ namespace Duplicati.Library.Backend
req.Credentials = m_userInfo;
req.PreAuthenticate = true; // We need this under Mono for some reason, and it appears some servers require this as well
req.KeepAlive = false;
req.UserAgent = "Duplicati Jottacloud Client v" + System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString();
req.UserAgent = "Duplicati Jottacloud Client v" + System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
req.Headers.Add("x-jftp-version", API_VERSION);
return req;
}
@@ -27,7 +27,7 @@ namespace Duplicati.Library
{
public class JSONWebHelper
{
public static readonly string USER_AGENT = string.Format("Duplicati v{0}", System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString());
public static readonly string USER_AGENT = string.Format("Duplicati v{0}", System.Reflection.Assembly.GetExecutingAssembly().GetName().Version);
private readonly string m_user_agent;
public string OAuthLoginUrl { get; protected set; }
public string UserAgent { get { return m_user_agent; } }
@@ -21,7 +21,7 @@ namespace Duplicati.Library.Backend
private const long BITS_FILE_SIZE_LIMIT = 1024 * 1024 * 15;
private const long BITS_CHUNK_SIZE = 1024 * 1024 * 10;
private static readonly string USER_AGENT = string.Format("Duplicati v{0}", System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString());
private static readonly string USER_AGENT = string.Format("Duplicati v{0}", System.Reflection.Assembly.GetExecutingAssembly().GetName().Version);
private string m_rootfolder;
private string m_prefix;
@@ -42,7 +42,7 @@ namespace Duplicati.Library.Backend
m_rootfolder = uri.Host;
m_prefix = "/" + uri.Path;
if (!m_prefix.EndsWith("/"))
if (!m_prefix.EndsWith("/", StringComparison.Ordinal))
m_prefix += "/";
string authid = null;
@@ -188,11 +188,11 @@ namespace Duplicati.Library.Backend.OpenStack
m_container = uri.Host;
m_prefix = "/" + uri.Path;
if (!m_prefix.EndsWith("/"))
if (!m_prefix.EndsWith("/", StringComparison.Ordinal))
m_prefix += "/";
// For OpenStack we do not use a leading slash
if (m_prefix.StartsWith("/"))
if (m_prefix.StartsWith("/", StringComparison.Ordinal))
m_prefix = m_prefix.Substring(1);
options.TryGetValue(USERNAME_OPTION, out m_username);
@@ -232,7 +232,7 @@ namespace Duplicati.Library.Backend.OpenStack
private string JoinUrls(string uri, string fragment)
{
fragment = fragment ?? "";
return uri + (uri.EndsWith("/") ? "" : "/") + (fragment.StartsWith("/") ? fragment.Substring(1) : fragment);
return uri + (uri.EndsWith("/", StringComparison.Ordinal) ? "" : "/") + (fragment.StartsWith("/", StringComparison.Ordinal) ? fragment.Substring(1) : fragment);
}
private string JoinUrls(string uri, string fragment1, string fragment2)
{
@@ -338,7 +338,7 @@ namespace Duplicati.Library.Backend.OpenStack
foreach (var n in items)
{
var name = n.name;
if (name.StartsWith(m_prefix))
if (name.StartsWith(m_prefix, StringComparison.Ordinal))
name = name.Substring(m_prefix.Length);
if (n.bytes == null)
+8 -8
View File
@@ -218,25 +218,25 @@ namespace Duplicati.Library.Backend
{
m_bucket = Library.Utility.Uri.UrlDecode(u.PathAndQuery);
if (m_bucket.StartsWith("/"))
if (m_bucket.StartsWith("/", StringComparison.Ordinal))
m_bucket = m_bucket.Substring(1);
if (m_bucket.Contains("/"))
{
m_prefix = m_bucket.Substring(m_bucket.IndexOf("/") + 1);
m_bucket = m_bucket.Substring(0, m_bucket.IndexOf("/"));
m_prefix = m_bucket.Substring(m_bucket.IndexOf("/", StringComparison.Ordinal) + 1);
m_bucket = m_bucket.Substring(0, m_bucket.IndexOf("/", StringComparison.Ordinal));
}
}
else
{
//Subdomain type lookup
if (host.ToLower().EndsWith("." + s3host))
if (host.ToLower().EndsWith("." + s3host, StringComparison.Ordinal))
{
m_bucket = host.Substring(0, host.Length - ("." + s3host).Length);
host = s3host;
m_prefix = Library.Utility.Uri.UrlDecode(u.PathAndQuery);
if (m_prefix.StartsWith("/"))
if (m_prefix.StartsWith("/", StringComparison.Ordinal))
m_prefix = m_prefix.Substring(1);
}
else
@@ -255,7 +255,7 @@ namespace Duplicati.Library.Backend
m_options = options;
m_prefix = m_prefix.Trim();
if (m_prefix.Length != 0 && !m_prefix.EndsWith("/"))
if (m_prefix.Length != 0 && !m_prefix.EndsWith("/", StringComparison.Ordinal))
m_prefix += "/";
// Auto-disable dns lookup for non AWS configurations
@@ -315,7 +315,7 @@ namespace Duplicati.Library.Backend
((FileEntry)file).Name = file.Name.Substring(m_prefix.Length);
//Fix for a bug in Duplicati 1.0 beta 3 and earlier, where filenames are incorrectly prefixed with a slash
if (file.Name.StartsWith("/") && !m_prefix.StartsWith("/"))
if (file.Name.StartsWith("/", StringComparison.Ordinal) && !m_prefix.StartsWith("/", StringComparison.Ordinal))
((FileEntry)file).Name = file.Name.Substring(1);
yield return file;
@@ -362,7 +362,7 @@ namespace Duplicati.Library.Backend
//This is a fix for the S3 backend prior to beta 3, where the filenames had a slash prefixed
try
{
if (!remotename.StartsWith("/"))
if (!remotename.StartsWith("/", StringComparison.Ordinal))
Connection.GetFileStream(m_bucket, GetFullKey("/" + remotename), output);
return;
}
@@ -99,7 +99,7 @@ namespace Duplicati.Library.Backend
}
var keys = authorized_keys == null ? new string[0] : authorized_keys.Split(new char[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
var cleaned_keys = keys.Select(x => x.Trim()).Where(x => x.Length > 0 && !x.StartsWith("#"));
var cleaned_keys = keys.Select(x => x.Trim()).Where(x => x.Length > 0 && !x.StartsWith("#", StringComparison.Ordinal));
// Does the key already exist?
if (cleaned_keys.Where(x =>
+25 -18
View File
@@ -78,10 +78,10 @@ namespace Duplicati.Library.Backend
m_path = uri.Path;
if (!string.IsNullOrWhiteSpace(m_path) && !m_path.EndsWith("/"))
if (!string.IsNullOrWhiteSpace(m_path) && !m_path.EndsWith("/", StringComparison.Ordinal))
m_path += "/";
if (!m_path.StartsWith("/"))
if (!m_path.StartsWith("/", StringComparison.Ordinal))
m_path = "/" + m_path;
m_server = uri.Host;
@@ -114,7 +114,7 @@ namespace Duplicati.Library.Backend
CreateConnection();
//Bugfix, some SSH servers do not like a trailing slash
string p = m_path;
if (p.EndsWith("/"))
if (p.EndsWith("/", StringComparison.Ordinal))
p.Substring(0, p.Length - 1);
m_con.CreateDirectory(p);
}
@@ -297,7 +297,7 @@ namespace Duplicati.Library.Backend
string working_dir = m_con.WorkingDirectory;
if (!working_dir.EndsWith("/"))
if (!working_dir.EndsWith("/", StringComparison.Ordinal))
working_dir += "/";
if (working_dir == path)
@@ -322,33 +322,40 @@ namespace Duplicati.Library.Backend
foreach (Renci.SshNet.Sftp.SftpFile ls in m_con.ListDirectory(path))
if (ls.Name.ToString() != "." && ls.Name.ToString() != "..")
yield return new FileEntry(ls.Name.ToString(), ls.Length, ls.LastAccessTime, ls.LastWriteTime) { IsFolder = ls.Attributes.IsDirectory };
yield return new FileEntry(ls.Name, ls.Length, ls.LastAccessTime, ls.LastWriteTime) { IsFolder = ls.Attributes.IsDirectory };
}
public static Renci.SshNet.PrivateKeyFile ValidateKeyFile(string filename, string password)
{
if (filename.StartsWith(KEYFILE_URI, StringComparison.OrdinalIgnoreCase))
try
{
using (var ms = new System.IO.MemoryStream())
using (var sr = new System.IO.StreamWriter(ms))
if (filename.StartsWith(KEYFILE_URI, StringComparison.OrdinalIgnoreCase))
{
sr.Write(Duplicati.Library.Utility.Uri.UrlDecode(filename.Substring(KEYFILE_URI.Length)));
sr.Flush();
using (var ms = new System.IO.MemoryStream())
using (var sr = new System.IO.StreamWriter(ms))
{
sr.Write(Duplicati.Library.Utility.Uri.UrlDecode(filename.Substring(KEYFILE_URI.Length)));
sr.Flush();
ms.Position = 0;
ms.Position = 0;
if (String.IsNullOrEmpty(password))
return new Renci.SshNet.PrivateKeyFile(ms);
else
return new Renci.SshNet.PrivateKeyFile(ms, password);
}
}
else
{
if (String.IsNullOrEmpty(password))
return new Renci.SshNet.PrivateKeyFile(ms);
return new Renci.SshNet.PrivateKeyFile(filename);
else
return new Renci.SshNet.PrivateKeyFile(ms, password);
return new Renci.SshNet.PrivateKeyFile(filename, password);
}
}
else
catch (Exception ex)
{
if (String.IsNullOrEmpty(password))
return new Renci.SshNet.PrivateKeyFile(filename);
else
return new Renci.SshNet.PrivateKeyFile(filename, password);
throw new UserInformationException(string.Format("Failed to parse the keyfile, check the key format and passphrase. Error message was {0}", ex.Message), ex);
}
}
@@ -162,9 +162,9 @@ namespace Duplicati.Library.Backend
m_spWebUrl = null;
m_serverRelPath = u.Path;
if (!m_serverRelPath.StartsWith("/"))
if (!m_serverRelPath.StartsWith("/", StringComparison.Ordinal))
m_serverRelPath = "/" + m_serverRelPath;
if (!m_serverRelPath.EndsWith("/"))
if (!m_serverRelPath.EndsWith("/", StringComparison.Ordinal))
m_serverRelPath += "/";
// remove marker for SP-Web
m_serverRelPath = m_serverRelPath.Replace("//", "/");
@@ -290,7 +290,7 @@ namespace Duplicati.Library.Backend
retCtx = null;
string path = orgUrl.Path;
int webIndicatorPos = path.IndexOf("//");
int webIndicatorPos = path.IndexOf("//", StringComparison.Ordinal);
// if a hint is supplied, we will of course use this first.
if (webIndicatorPos >= 0)
@@ -364,7 +364,6 @@ namespace Duplicati.Library.Backend
/// We have to check for the exceptions thrown to know about file /folder existence.
/// Why the funny guys at MS provided an .Exists field stays a mystery...
/// </summary>
/// <param name="fileNameInfo"> the filename </param>
private void wrappedExecuteQueryOnConext(SP.ClientContext ctx, string serverRelPathInfo, bool isFolder)
{
try { ctx.ExecuteQuery(); }
+4 -4
View File
@@ -46,9 +46,9 @@ namespace Duplicati.Library.Backend.Sia
}
while(m_targetpath.Contains("//"))
m_targetpath = m_targetpath.Replace("//","/");
while (m_targetpath.StartsWith("/"))
while (m_targetpath.StartsWith("/", StringComparison.Ordinal))
m_targetpath = m_targetpath.Substring(1);
while (m_targetpath.EndsWith("/"))
while (m_targetpath.EndsWith("/", StringComparison.Ordinal))
m_targetpath = m_targetpath.Remove(m_targetpath.Length - 1);
if (m_targetpath.Length == 0)
@@ -73,7 +73,7 @@ namespace Duplicati.Library.Backend.Sia
}
req.KeepAlive = false;
req.UserAgent = string.Format("Sia-Agent (Duplicati SIA client {0})", System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString());
req.UserAgent = string.Format("Sia-Agent (Duplicati SIA client {0})", System.Reflection.Assembly.GetExecutingAssembly().GetName().Version);
return req;
}
@@ -294,7 +294,7 @@ namespace Duplicati.Library.Backend.Sia
{
// Sia returns a complete file list, but we're only interested in files that are
// in our target path
if (f.Siapath.StartsWith(m_targetpath))
if (f.Siapath.StartsWith(m_targetpath, StringComparison.Ordinal))
{
FileEntry fe = new FileEntry(f.Siapath.Substring(m_targetpath.Length + 1));
fe.Size = f.Filesize;
@@ -99,13 +99,13 @@ namespace Duplicati.Library.Backend
var u = new Utility.Uri(url);
u.RequireHost();
if (!u.Path.StartsWith("uri/URI:DIR2:") && !u.Path.StartsWith("uri/URI%3ADIR2%3A"))
if (!u.Path.StartsWith("uri/URI:DIR2:", StringComparison.Ordinal) && !u.Path.StartsWith("uri/URI%3ADIR2%3A", StringComparison.Ordinal))
throw new UserInformationException(Strings.TahoeBackend.UnrecognizedUriError);
m_useSSL = Utility.Utility.ParseBoolOption(options, "use-ssl");
m_url = u.SetScheme(m_useSSL ? "https" : "http").SetQuery(null).SetCredentials(null, null).ToString();
if (!m_url.EndsWith("/"))
if (!m_url.EndsWith("/", StringComparison.Ordinal))
m_url += "/";
}
@@ -114,7 +114,7 @@ namespace Duplicati.Library.Backend
System.Net.HttpWebRequest req = (System.Net.HttpWebRequest)System.Net.HttpWebRequest.Create(m_url + (Library.Utility.Uri.UrlEncode(remotename).Replace("+", "%20")) + (string.IsNullOrEmpty(queryparams) || queryparams.Trim().Length == 0 ? "" : "?" + queryparams));
req.KeepAlive = false;
req.UserAgent = "Duplicati Tahoe-LAFS Client v" + System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString();
req.UserAgent = "Duplicati Tahoe-LAFS Client v" + System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
return req;
}
+11 -11
View File
@@ -91,13 +91,13 @@ namespace Duplicati.Library.Backend
m_useSSL = Utility.Utility.ParseBoolOption(options, "use-ssl");
m_url = u.SetScheme(m_useSSL ? "https" : "http").SetCredentials(null, null).SetQuery(null).ToString();
if (!m_url.EndsWith("/"))
if (!m_url.EndsWith("/", StringComparison.Ordinal))
m_url += "/";
m_path = u.Path;
if (!m_path.StartsWith("/"))
if (!m_path.StartsWith("/", StringComparison.Ordinal))
m_path = "/" + m_path;
if (!m_path.EndsWith("/"))
if (!m_path.EndsWith("/", StringComparison.Ordinal))
m_path += "/";
m_path = Library.Utility.Uri.UrlDecode(m_path);
@@ -195,19 +195,19 @@ namespace Duplicati.Library.Backend
//TODO: This list is getting ridiculous, should change to regexps
if (name.StartsWith(m_url))
if (name.StartsWith(m_url, StringComparison.Ordinal))
cmp_path = m_url;
else if (name.StartsWith(m_rawurl))
else if (name.StartsWith(m_rawurl, StringComparison.Ordinal))
cmp_path = m_rawurl;
else if (name.StartsWith(m_rawurlPort))
else if (name.StartsWith(m_rawurlPort, StringComparison.Ordinal))
cmp_path = m_rawurlPort;
else if (name.StartsWith(m_path))
else if (name.StartsWith(m_path, StringComparison.Ordinal))
cmp_path = m_path;
else if (name.StartsWith("/" + m_path))
else if (name.StartsWith("/" + m_path, StringComparison.Ordinal))
cmp_path = "/" + m_path;
else if (name.StartsWith(m_sanitizedUrl))
else if (name.StartsWith(m_sanitizedUrl, StringComparison.Ordinal))
cmp_path = m_sanitizedUrl;
else if (name.StartsWith(m_reverseProtocolUrl))
else if (name.StartsWith(m_reverseProtocolUrl, StringComparison.Ordinal))
cmp_path = m_reverseProtocolUrl;
else
continue;
@@ -360,7 +360,7 @@ namespace Duplicati.Library.Backend
}
req.KeepAlive = false;
req.UserAgent = "Duplicati WEBDAV Client v" + System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString();
req.UserAgent = "Duplicati WEBDAV Client v" + System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
return req;
}
+63 -79
View File
@@ -24,7 +24,6 @@ using Duplicati.Library.Interface;
using SharpCompress.Common;
using SharpCompress.Archives;
using SharpCompress.Archives.Zip;
using SharpCompress.Writers;
using SharpCompress.Writers.Zip;
using SharpCompress.Readers;
@@ -34,10 +33,13 @@ namespace Duplicati.Library.Compression
{
/// <summary>
/// An abstraction of a zip archive as a FileArchive, based on SharpCompress.
/// Please note, duplicati does not require both Read & Write access at the same time so this has not been implemented
/// Please note, duplicati does not require both Read & Write access at the same time so this has not been implemented.
/// </summary>
public class FileArchiveZip : ICompression
{
private const string CannotReadWhileWriting = "Cannot read while writing";
private const string CannotWriteWhileReading = "Cannot write while reading";
/// <summary>
/// The commandline option for toggling the compression level
/// </summary>
@@ -51,16 +53,16 @@ namespace Duplicati.Library.Compression
/// <summary>
/// The commandline option for toggling the compression method
/// </summary>
private const string COMPRESSION_METHOD_OPTION = "zip-compression-method";
/// <summary>
/// The commandline option for toggling the zip64 support
/// </summary>
private const string COMPRESSION_ZIP64_OPTION = "zip-compression-zip64";
/// <summary>
/// The default compression level
/// </summary>
private const SharpCompress.Compressors.Deflate.CompressionLevel DEFAULT_COMPRESSION_LEVEL = SharpCompress.Compressors.Deflate.CompressionLevel.Level9;
private const string COMPRESSION_METHOD_OPTION = "zip-compression-method";
/// <summary>
/// The commandline option for toggling the zip64 support
/// </summary>
private const string COMPRESSION_ZIP64_OPTION = "zip-compression-zip64";
/// <summary>
/// The default compression level
/// </summary>
private const SharpCompress.Compressors.Deflate.CompressionLevel DEFAULT_COMPRESSION_LEVEL = SharpCompress.Compressors.Deflate.CompressionLevel.Level9;
/// <summary>
/// The default compression method
@@ -83,9 +85,9 @@ namespace Duplicati.Library.Compression
private const int CENTRAL_HEADER_ENTRY_SIZE_ZIP64_EXTRA = 2 + 2 + 8 + 8 + 8 + 4;
/// <summary>
/// This property indicates that this current instance should write to a file
/// This property indicates reading or writing access mode of the file archive.
/// </summary>
private bool m_isWriting;
ArchiveMode m_mode;
/// <summary>
/// Gets the number of bytes expected to be written after the stream is disposed
@@ -100,12 +102,12 @@ namespace Duplicati.Library.Compression
/// The stream used to either read or write
/// </summary>
private Stream m_stream;
/// <summary>
/// Lookup table for faster access to entries based on their name.
/// </summary>
private Dictionary<string, IEntry> m_entryDict;
/// <summary>
/// The writer instance used when creating archives
/// </summary>
@@ -119,18 +121,13 @@ namespace Duplicati.Library.Compression
/// <summary>
/// The compression level applied when the hint does not indicate incompressible
/// </summary>
private SharpCompress.Compressors.Deflate.CompressionLevel m_defaultCompressionLevel;
private SharpCompress.Compressors.Deflate.CompressionLevel m_defaultCompressionLevel;
/// <summary>
/// <summary>
/// The compression level applied when the hint does not indicate incompressible
/// </summary>
private CompressionType m_compressionType;
/// <summary>
/// The name of the file being read
/// </summary>
private string m_filename;
/// <summary>
/// A flag indicating if zip64 is in use
/// </summary>
@@ -141,14 +138,15 @@ namespace Duplicati.Library.Compression
/// </summary>
public FileArchiveZip() { }
public IArchive Archive
private IArchive Archive
{
get
{
if (m_stream == null)
m_stream = new System.IO.FileStream(m_filename, FileMode.Open, FileAccess.Read, FileShare.Read);
if (m_archive == null)
{
m_stream.Position = 0;
m_archive = ArchiveFactory.Open(m_stream);
}
return m_archive;
}
}
@@ -168,19 +166,16 @@ namespace Duplicati.Library.Compression
public Stream GetStreamFromReader(IEntry entry)
{
Stream fs = null;
SharpCompress.Readers.Zip.ZipReader rd = null;
try
{
fs = new System.IO.FileStream(m_filename, FileMode.Open, FileAccess.Read, FileShare.Read);
rd = SharpCompress.Readers.Zip.ZipReader.Open(fs);
rd = SharpCompress.Readers.Zip.ZipReader.Open(m_stream);
while (rd.MoveToNextEntry())
if (entry.Key == rd.Entry.Key)
return new StreamWrapper(rd.OpenEntryStream(), stream => {
rd.Dispose();
fs.Dispose();
});
throw new Exception(string.Format("Stream not found: {0}", entry.Key));
@@ -189,9 +184,7 @@ namespace Duplicati.Library.Compression
{
if (rd != null)
rd.Dispose();
if (fs != null)
fs.Dispose();
throw;
}
@@ -199,22 +192,18 @@ namespace Duplicati.Library.Compression
/// <summary>
/// Constructs a new zip instance.
/// If the file exists and has a non-zero length we read it,
/// otherwise we create a new archive.
/// Access mode is specified by mode parameter.
/// Note that stream would not be disposed by FileArchiveZip instance so
/// you may reuse it and have to dispose it yourself.
/// </summary>
/// <param name="filename">The name of the file to read or write</param>
/// <param name="stream">The stream to read or write depending access mode</param>
/// <param name="mode">The archive acces mode</param>
/// <param name="options">The options passed on the commandline</param>
public FileArchiveZip(string filename, Dictionary<string, string> options)
public FileArchiveZip(Stream stream, ArchiveMode mode, IDictionary<string, string> options)
{
if (string.IsNullOrEmpty(filename) && filename.Trim().Length == 0)
throw new ArgumentException("Invalid filename.", nameof(filename));
if (File.Exists(filename) && new FileInfo(filename).Length > 0)
{
m_isWriting = false;
m_filename = filename;
}
else
m_stream = stream;
m_mode = mode;
if (mode == ArchiveMode.Write)
{
var compression = new ZipWriterOptions(CompressionType.Deflate);
@@ -241,8 +230,6 @@ namespace Duplicati.Library.Compression
m_defaultCompressionLevel = compression.DeflateCompressionLevel;
m_compressionType = compression.CompressionType;
m_isWriting = true;
m_stream = new System.IO.FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.Read);
m_writer = WriterFactory.Open(m_stream, ArchiveType.Zip, compression);
//Size of endheader, taken from SharpCompress ZipWriter
@@ -270,26 +257,26 @@ namespace Duplicati.Library.Compression
public IList<ICommandLineArgument> SupportedCommands
{
get
{
// This is the cross between these two:
// https://github.com/adamhathcock/sharpcompress/blob/master/src/SharpCompress/Common/Zip/ZipCompressionMethod.cs
// https://github.com/adamhathcock/sharpcompress/blob/master/src/SharpCompress/Common/CompressionType.cs
var methods = new[]
{
// This is the cross between these two:
// https://github.com/adamhathcock/sharpcompress/blob/master/src/SharpCompress/Common/Zip/ZipCompressionMethod.cs
// https://github.com/adamhathcock/sharpcompress/blob/master/src/SharpCompress/Common/CompressionType.cs
var methods = new[]
{
CompressionType.None.ToString(),
CompressionType.Deflate.ToString(),
CompressionType.BZip2.ToString(),
CompressionType.LZMA.ToString(),
CompressionType.PPMd.ToString(),
CompressionType.None.ToString(),
CompressionType.Deflate.ToString(),
CompressionType.BZip2.ToString(),
CompressionType.LZMA.ToString(),
CompressionType.PPMd.ToString(),
};
return new List<ICommandLineArgument>(new ICommandLineArgument[] {
new CommandLineArgument(COMPRESSION_LEVEL_OPTION, CommandLineArgument.ArgumentType.Enumeration, Strings.FileArchiveZip.CompressionlevelShort, Strings.FileArchiveZip.CompressionlevelLong, DEFAULT_COMPRESSION_LEVEL.ToString(), null, new string[] {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"}),
new CommandLineArgument(COMPRESSION_LEVEL_OPTION_ALIAS, CommandLineArgument.ArgumentType.Enumeration, Strings.FileArchiveZip.CompressionlevelShort, Strings.FileArchiveZip.CompressionlevelLong, DEFAULT_COMPRESSION_LEVEL.ToString(), null, new string[] {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"}, Strings.FileArchiveZip.CompressionlevelDeprecated(COMPRESSION_LEVEL_OPTION)),
new CommandLineArgument(COMPRESSION_METHOD_OPTION, CommandLineArgument.ArgumentType.Enumeration, Strings.FileArchiveZip.CompressionmethodShort, Strings.FileArchiveZip.CompressionmethodLong(COMPRESSION_LEVEL_OPTION), DEFAULT_COMPRESSION_METHOD.ToString(), null, methods),
new CommandLineArgument(COMPRESSION_ZIP64_OPTION, CommandLineArgument.ArgumentType.Boolean, Strings.FileArchiveZip.Compressionzip64Short, Strings.FileArchiveZip.Compressionzip64Long, DEFAULT_ZIP64.ToString())
});
new CommandLineArgument(COMPRESSION_ZIP64_OPTION, CommandLineArgument.ArgumentType.Boolean, Strings.FileArchiveZip.Compressionzip64Short, Strings.FileArchiveZip.Compressionzip64Long, DEFAULT_ZIP64.ToString())
});
}
}
@@ -329,8 +316,8 @@ namespace Duplicati.Library.Compression
/// <returns>A stream with the file contents</returns>
public Stream OpenRead(string file)
{
if (m_isWriting)
throw new InvalidOperationException("Cannot read while writing");
if (m_mode != ArchiveMode.Read)
throw new InvalidOperationException(CannotReadWhileWriting);
var ze = GetEntry(file);
if (ze == null)
@@ -373,8 +360,7 @@ namespace Duplicati.Library.Compression
try
{
using (var fs = new System.IO.FileStream(m_filename, FileMode.Open, FileAccess.Read, FileShare.Read))
using (var rd = SharpCompress.Readers.Zip.ZipReader.Open(fs, new ReaderOptions() { LookForHeader = false }))
using (var rd = SharpCompress.Readers.Zip.ZipReader.Open(m_stream, new ReaderOptions() { LookForHeader = false }))
while (rd.MoveToNextEntry())
{
d[rd.Entry.Key] = rd.Entry;
@@ -390,10 +376,10 @@ namespace Duplicati.Library.Compression
// If we have zero files, or just a manifest, don't bother
if (d.Count < 2)
throw;
Logging.Log.WriteMessage(string.Format("Zip archive appears to have broken records, returning the {0} records that could be recovered", d.Count), Logging.LogMessageType.Warning, ex2);
}
m_entryDict = d;
}
}
@@ -406,8 +392,8 @@ namespace Duplicati.Library.Compression
/// <returns>The ZipEntry for the file or null if no such file was found</returns>
private IEntry GetEntry(string file)
{
if (m_isWriting)
throw new InvalidOperationException("Cannot read while writing");
if (m_mode != ArchiveMode.Read)
throw new InvalidOperationException(CannotReadWhileWriting);
LoadEntryTable();
@@ -429,13 +415,13 @@ namespace Duplicati.Library.Compression
/// <returns>A writeable stream for the file contents</returns>
public virtual Stream CreateFile(string file, CompressionHint hint, DateTime lastWrite)
{
if (!m_isWriting)
throw new InvalidOperationException("Cannot write while reading");
if (m_mode != ArchiveMode.Write)
throw new InvalidOperationException(CannotWriteWhileReading);
m_flushBufferSize += CENTRAL_HEADER_ENTRY_SIZE + System.Text.Encoding.UTF8.GetByteCount(file);
if (m_usingZip64)
m_flushBufferSize += CENTRAL_HEADER_ENTRY_SIZE_ZIP64_EXTRA;
return ((ZipWriter)m_writer).WriteToStream(file, new ZipWriterEntryOptions()
{
DeflateCompressionLevel = hint == CompressionHint.Noncompressible ? SharpCompress.Compressors.Deflate.CompressionLevel.None : m_defaultCompressionLevel,
@@ -452,8 +438,8 @@ namespace Duplicati.Library.Compression
/// <returns>True if the file exists, false otherwise</returns>
public bool FileExists(string file)
{
if (m_isWriting)
throw new InvalidOperationException("Cannot read while writing");
if (m_mode != ArchiveMode.Read)
throw new InvalidOperationException(CannotReadWhileWriting);
return GetEntry(file) != null;
}
@@ -465,7 +451,7 @@ namespace Duplicati.Library.Compression
{
get
{
return m_isWriting ? m_stream.Length : Archive.TotalSize;
return m_mode == ArchiveMode.Write ? m_stream.Length : Archive.TotalSize;
}
}
@@ -473,11 +459,11 @@ namespace Duplicati.Library.Compression
/// The size of the current unflushed buffer
/// </summary>
public long FlushBufferSize
{
{
get
{
return m_flushBufferSize;
}
}
}
@@ -514,8 +500,6 @@ namespace Duplicati.Library.Compression
m_writer.Dispose();
m_writer = null;
if (m_stream != null)
m_stream.Dispose();
m_stream = null;
}
@@ -2,17 +2,17 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using Duplicati.Library.Interface;
using Duplicati.Library.Utility;
namespace Duplicati.Library.Compression
{
public class SevenZipCompression: ICompressionHinting
public class SevenZipCompression : ICompressionHinting
{
// next file starts a new stream if the previous stream is larger than this
private const int kStreamThreshold = 1 << 20; // 1 MB
private FileStream m_file;
private Stream m_stream;
private ManagedLzma.LZMA.Master.SevenZip.ArchiveWriter m_writer;
private ManagedLzma.LZMA.Master.SevenZip.ArchiveWriter.PlainEncoder m_copyEncoder;
private ManagedLzma.LZMA.Master.SevenZip.ArchiveWriter.ThreadedEncoder m_lzma2Encoder;
@@ -33,9 +33,9 @@ namespace Duplicati.Library.Compression
get { return m_lowOverheadMode; }
set
{
if(m_lowOverheadMode != value)
if (m_lowOverheadMode != value)
{
if(m_lzma2Encoder != null)
if (m_lzma2Encoder != null)
throw new NotSupportedException("Cannot change LowOverheadMode after writing has started.");
m_lowOverheadMode = value;
@@ -50,29 +50,46 @@ namespace Duplicati.Library.Compression
/// <summary>
/// Constructs a new zip instance.
/// If the file exists and has a non-zero length we read it,
/// otherwise we create a new archive.
/// Access mode is specified by mode parameter.
/// Note that stream would not be disposed by FileArchiveZip instance so
/// you may reuse it and have to dispose it yourself.
/// </summary>
/// <param name="filename">The name of the file to read or write</param>
/// <param name="stream">The stream to read or write depending access mode</param>
/// <param name="mode">The archive acces mode</param>
/// <param name="options">The options passed on the commandline</param>
public SevenZipCompression(string filename, Dictionary<string, string> options)
public SevenZipCompression(Stream stream, ArchiveMode mode, IDictionary<string, string> options)
{
InitializeCompression(options);
// Preventing the stream being closed by ArchiveWriter so it could be reused.
// Required for using with MemoryStream.
m_stream = new ShaderStream(stream, true);
if (mode == ArchiveMode.Write)
InitializeArchiveWriter();
else
{
stream.Position = 0;
InitializeArchiveReader();
}
}
private void InitializeCompression(IDictionary<string, string> options)
{
m_threadCount = DEFAULT_THREAD_COUNT;
string threadCountSetting;
int threadCountValue;
if(options != null
if (options != null
&& options.TryGetValue(THREAD_COUNT_OPTION, out threadCountSetting)
&& Int32.TryParse(threadCountSetting, out threadCountValue)
&& threadCountValue > 0)
{
// arbitrary limit to avoid stupid mistakes
if(threadCountValue > MAX_THREAD_COUNT)
if (threadCountValue > MAX_THREAD_COUNT)
threadCountValue = MAX_THREAD_COUNT;
m_threadCount = threadCountValue;
}
string cplvl;
int tmplvl;
if (options.TryGetValue(COMPRESSION_LEVEL_OPTION, out cplvl) && int.TryParse(cplvl, out tmplvl))
@@ -85,67 +102,59 @@ namespace Duplicati.Library.Compression
m_encoderProps.mLzmaProps.mLevel = tmplvl;
m_encoderProps.mLzmaProps.mAlgo = Library.Utility.Utility.ParseBoolOption(options, COMPRESSION_FASTALGO_OPTION) ? 0 : 1;
var file = new FileInfo(filename);
if(file.Exists && file.Length > 0)
InitializeArchiveReader(filename);
else
InitializeArchiveWriter(filename);
}
private void InitializeArchiveReader(string file)
private void InitializeArchiveReader()
{
m_file = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Delete);
m_archive = new master._7zip.Legacy.CArchiveDatabaseEx();
m_reader = new master._7zip.Legacy.ArchiveReader();
m_reader.Open(m_file);
m_reader.Open(m_stream);
m_reader.ReadDatabase(m_archive, null);
m_archive.Fill();
}
private void InitializeArchiveWriter(string file)
private void InitializeArchiveWriter()
{
m_file = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.Delete);
m_writer = new ManagedLzma.LZMA.Master.SevenZip.ArchiveWriter(m_file);
m_writer = new ManagedLzma.LZMA.Master.SevenZip.ArchiveWriter(m_stream);
}
public void Dispose()
{
if(m_archive != null)
if (m_archive != null)
m_archive = null;
if(m_reader != null)
if (m_reader != null)
try { m_reader.Close(); }
finally { m_reader = null; }
if(m_writer != null)
if (m_writer != null)
{
try
{
if(m_copyEncoder != null)
if (m_copyEncoder != null)
try
{
m_writer.ConnectEncoder(m_copyEncoder);
m_copyEncoder.Dispose();
}
finally { m_copyEncoder = null; }
if(m_lzma2Encoder != null)
if (m_lzma2Encoder != null)
try
{
m_writer.ConnectEncoder(m_lzma2Encoder);
m_lzma2Encoder.Dispose();
}
finally { m_lzma2Encoder = null; }
m_writer.WriteFinalHeader();
}
finally { m_writer = null; }
}
if(m_file != null)
try { m_file.Dispose(); }
finally { m_file = null; }
if (m_stream != null)
try { m_stream.Dispose(); }
finally { m_stream = null; }
}
#region ICompression - Reader Methods
@@ -162,26 +171,26 @@ namespace Duplicati.Library.Compression
public IEnumerable<KeyValuePair<string, long>> ListFilesWithSize(string prefix)
{
if(m_reader == null)
if (m_reader == null)
throw new InvalidOperationException(Strings.SevenZipCompression.NoReaderError);
StringComparison sc = Library.Utility.Utility.ClientFilenameStringComparision;
return
return
from n in m_reader.GetFiles(m_archive)
where
!n.IsDir &&
(prefix == null || n.Name.StartsWith(prefix, sc))
select new KeyValuePair<string, long>(n.Name, n.Size);
where
!n.IsDir &&
(prefix == null || n.Name.StartsWith(prefix, sc))
select new KeyValuePair<string, long>(n.Name, n.Size);
}
public DateTime GetLastWriteTime(string file)
{
var item = m_reader.GetFiles(m_archive).FirstOrDefault(x => x.Name == file);
if(item == null)
if (item == null)
throw new FileNotFoundException(Strings.SevenZipCompression.FileNotFoundError, file);
if(!item.MTime.HasValue)
if (!item.MTime.HasValue)
return new DateTime(0);
return item.MTime.Value;
@@ -189,14 +198,14 @@ namespace Duplicati.Library.Compression
public Stream OpenRead(string file)
{
if(string.IsNullOrEmpty(file))
throw new ArgumentNullException("file");
if (string.IsNullOrEmpty(file))
throw new ArgumentNullException(nameof(file));
if(m_reader == null)
if (m_reader == null)
throw new InvalidOperationException(Strings.SevenZipCompression.NoReaderError);
var item = m_reader.GetFiles(m_archive).FirstOrDefault(x => x.Name == file);
if(item == null)
if (item == null)
throw new FileNotFoundException(Strings.SevenZipCompression.FileNotFoundError, file);
return m_reader.OpenStream(m_archive, m_reader.GetFileIndex(m_archive, item), null);
@@ -211,7 +220,7 @@ namespace Duplicati.Library.Compression
m_writer.ConnectEncoder(m_lzma2Encoder);
}
private sealed class WriterEntry: ManagedLzma.LZMA.Master.SevenZip.IArchiveWriterEntry
private sealed class WriterEntry : ManagedLzma.LZMA.Master.SevenZip.IArchiveWriterEntry
{
private string mName;
private DateTime? mTimestamp;
@@ -254,19 +263,19 @@ namespace Duplicati.Library.Compression
public Stream CreateFile(string file, CompressionHint hint, DateTime lastWrite)
{
if(string.IsNullOrEmpty(file))
throw new ArgumentNullException("file");
if (string.IsNullOrEmpty(file))
throw new ArgumentNullException(nameof(file));
if(m_writer == null)
if (m_writer == null)
throw new InvalidOperationException(Strings.SevenZipCompression.NoWriterError);
var entry = new WriterEntry(file, lastWrite);
if(hint != CompressionHint.Noncompressible)
if (hint != CompressionHint.Noncompressible)
{
if(m_lzma2Encoder == null)
if (m_lzma2Encoder == null)
{
if(m_lowOverheadMode)
if (m_lowOverheadMode)
m_lzma2Encoder = new ManagedLzma.LZMA.Master.SevenZip.ArchiveWriter.LzmaEncoder();
else
m_lzma2Encoder = new ManagedLzma.LZMA.Master.SevenZip.ArchiveWriter.Lzma2Encoder(m_threadCount, m_encoderProps);
@@ -279,13 +288,13 @@ namespace Duplicati.Library.Compression
}
else
{
if(m_copyEncoder == null)
if (m_copyEncoder == null)
m_copyEncoder = new ManagedLzma.LZMA.Master.SevenZip.ArchiveWriter.PlainEncoder();
if(m_lzma2Encoder != null && m_lzma2Encoder == m_writer.CurrentEncoder)
if (m_lzma2Encoder != null && m_lzma2Encoder == m_writer.CurrentEncoder)
m_lzma2Encoder.SetOutputThreshold(kStreamThreshold); // rearm threshold so we can switch back
if(m_writer.CurrentEncoder != m_copyEncoder)
if (m_writer.CurrentEncoder != m_copyEncoder)
m_writer.ConnectEncoder(m_copyEncoder);
return m_copyEncoder.BeginWriteFile(entry);
@@ -296,10 +305,10 @@ namespace Duplicati.Library.Compression
{
get
{
if(m_writer != null)
if (m_writer != null)
return m_writer.WrittenSize;
else if(m_reader != null)
return m_file.Length;
else if (m_reader != null)
return m_stream.Length;
else
throw new InvalidOperationException();
}
@@ -309,17 +318,17 @@ namespace Duplicati.Library.Compression
{
get
{
if(m_writer != null)
if (m_writer != null)
{
long remaining = m_writer.CurrentSizeLimit - m_writer.WrittenSize;
if(m_copyEncoder != null && m_copyEncoder != m_writer.CurrentEncoder)
if (m_copyEncoder != null && m_copyEncoder != m_writer.CurrentEncoder)
remaining += m_copyEncoder.UpperBound;
if(m_lzma2Encoder != null && m_lzma2Encoder != m_writer.CurrentEncoder)
if (m_lzma2Encoder != null && m_lzma2Encoder != m_writer.CurrentEncoder)
remaining += m_lzma2Encoder.UpperBound;
return remaining;
}
else if(m_reader != null)
return m_file.Length;
else if (m_reader != null)
return m_stream.Length;
else
throw new InvalidOperationException();
}
@@ -392,12 +401,12 @@ namespace Duplicati.Library.Compression
DEFAULT_THREAD_COUNT.ToString()),
new CommandLineArgument(
COMPRESSION_LEVEL_OPTION,
CommandLineArgument.ArgumentType.Enumeration,
Strings.SevenZipCompression.CompressionlevelShort,
COMPRESSION_LEVEL_OPTION,
CommandLineArgument.ArgumentType.Enumeration,
Strings.SevenZipCompression.CompressionlevelShort,
Strings.SevenZipCompression.CompressionlevelLong,
DEFAULT_COMPRESSION_LEVEL.ToString(),
null,
DEFAULT_COMPRESSION_LEVEL.ToString(),
null,
new string[] {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"}),
new CommandLineArgument(
@@ -77,7 +77,7 @@ namespace Duplicati.Library.DynamicLoader
{
if (m_interfaces.ContainsKey(uri.Scheme))
return (IBackend)Activator.CreateInstance(m_interfaces[uri.Scheme].GetType(), url, newOpts);
else if (uri.Scheme.EndsWith("s"))
else if (uri.Scheme.EndsWith("s", StringComparison.Ordinal))
{
var tmpscheme = uri.Scheme.Substring(0, uri.Scheme.Length - 1);
if (m_interfaces.ContainsKey(tmpscheme))
@@ -123,7 +123,7 @@ namespace Duplicati.Library.DynamicLoader
IBackend b;
if (m_interfaces.TryGetValue(uri.Scheme, out b) && b != null)
return b.SupportedCommands;
else if (uri.Scheme.EndsWith("s"))
else if (uri.Scheme.EndsWith("s", StringComparison.Ordinal))
{
var tmpscheme = uri.Scheme.Substring(0, uri.Scheme.Length - 1);
if (m_interfaces.ContainsKey(tmpscheme))
@@ -19,6 +19,7 @@
#endregion
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using Duplicati.Library.Interface;
@@ -59,17 +60,17 @@ namespace Duplicati.Library.DynamicLoader
/// <param name="filename">The filename used to compress/decompress contents</param>
/// <param name="options">The options to pass to the instance constructor</param>
/// <returns>The instanciated encryption module or null if the file extension is not supported</returns>
public ICompression GetModule(string fileExtension, string filename, Dictionary<string, string> options)
public ICompression GetModule(string fileExtension, Stream stream, ArchiveMode mode, Dictionary<string, string> options)
{
if (string.IsNullOrEmpty(fileExtension))
throw new ArgumentNullException("fileExtension");
LoadInterfaces();
lock (m_lock)
{
if (m_interfaces.ContainsKey(fileExtension))
return (ICompression)Activator.CreateInstance(m_interfaces[fileExtension].GetType(), filename, options);
return (ICompression)Activator.CreateInstance(m_interfaces[fileExtension].GetType(), stream, mode, options);
else
return null;
}
@@ -125,17 +126,17 @@ namespace Duplicati.Library.DynamicLoader
return _compressionLoader.GetSupportedCommands(key);
}
/// <summary>
/// Instanciates a specific compression module, given the file extension and options
/// </summary>
/// <param name="fileExtension">The file extension to create the instance for</param>
/// <param name="filename">The filename of the file used to compress/decompress contents</param>
/// <param name="fileextension">The file extension to create the instance for</param>
/// <param name="stream">The stream of the file used to compress/decompress contents</param>
/// <param name="writing">True is the file opened for writing</param>
/// <param name="options">The options to pass to the instance constructor</param>
/// <returns>The instanciated compression module or null if the file extension is not supported</returns>
public static ICompression GetModule(string fileextension, string filename, Dictionary<string, string> options)
public static ICompression GetModule(string fileextension, Stream stream, ArchiveMode mode, Dictionary<string, string> options)
{
return _compressionLoader.GetModule(fileextension, filename, options);
return _compressionLoader.GetModule(fileextension, stream, mode, options);
}
#endregion
@@ -132,7 +132,7 @@ namespace Duplicati.Library.DynamicLoader
/// <summary>
/// Instanciates a specific encryption module, given the file extension and options
/// </summary>
/// <param name="fileExtension">The file extension to create the instance for</param>
/// <param name="fileextension">The file extension to create the instance for</param>
/// <param name="passphrase">The passphrase used to encrypt contents</param>
/// <param name="options">The options to pass to the instance constructor</param>
/// <returns>The instanciated encryption module or null if the file extension is not supported</returns>
@@ -67,7 +67,6 @@ namespace Duplicati.Library.Encryption
/// <summary>
/// Constructs a new AES encryption/decyption instance
/// </summary>
/// <param name="key">The key used for encryption. The key gets stretched through SHA hashing to fit the key size requirements</param>
public AESEncryption(string passphrase, Dictionary<string, string> options)
{
if(string.IsNullOrEmpty(passphrase))
@@ -222,7 +222,6 @@ namespace Duplicati.Library.Encryption
/// </summary>
/// <param name="args">The commandline arguments</param>
/// <param name="input">The input stream</param>
/// <param name="output">The output stream</param>
private System.IO.Stream Execute(string args, System.IO.Stream input, bool encrypt)
{
System.Diagnostics.ProcessStartInfo psi = new System.Diagnostics.ProcessStartInfo();
@@ -221,7 +221,6 @@ namespace Duplicati.Library.Interface
/// <param name="longDescription">The arguments long description</param>
/// <param name="defaultValue">The default value of the argumen</param>
/// <param name="aliases">A list of aliases for the command</param>
/// <param name="values">A list of valid values for the command</param>
public CommandLineArgument(string name, ArgumentType type, string shortDescription, string longDescription, string defaultValue, string[] aliases)
: this(name, type, shortDescription, longDescription, defaultValue)
{
+58 -37
View File
@@ -19,10 +19,26 @@
#endregion
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace Duplicati.Library.Interface
{
/// <summary>
/// Enumerates the possible modes for compression and decompression
/// </summary>
public enum ArchiveMode
{
/// <summary>
/// Indicates compression i.e. write archive mode, which means that the stream must be writeable
/// </summary>
Write,
/// <summary>
/// Indicates decompression i.e. read archive mode, which means that the stream must be readable
/// </summary>
Read
}
/// <summary>
/// A value that is given to the compressor as a hint
/// to how compressible the file is
@@ -43,9 +59,8 @@ namespace Duplicati.Library.Interface
/// Indicates that the files is incompressible
/// </summary>
Noncompressible
}
/// <summary>
/// An interface for passing additional hints to the compressor
/// about the expected contents of the volume
@@ -60,18 +75,7 @@ namespace Duplicati.Library.Interface
bool LowOverheadMode { get; set; }
}
/// <summary>
/// An interface for accessing files in an archive, such as a folder or compressed file.
/// All modules that implements compression must implement this interface.
/// The classes that implements this interface MUST also
/// implement a default constructor and a construtor that
/// has the signature new(string file, Dictionary&lt;string, string&gt; options).
/// The default constructor is used to construct an instance
/// so the DisplayName and other values can be read.
/// The other constructor is used to do the actual work.
/// The input file may not exist or have zero length, in which case it should be created.
/// </summary>
public interface ICompression : IDisposable
public interface IArchiveReader
{
/// <summary>
/// Returns all files in the archive, matching the prefix, if any.
@@ -86,35 +90,14 @@ namespace Duplicati.Library.Interface
/// <param name="prefix">An optional prefix for limiting the files returned</param>
/// <returns>All files in the archive, matching the prefix, if any</returns>
IEnumerable<KeyValuePair<string, long>> ListFilesWithSize(string prefix);
/// <summary>
/// Returns a stream with data from the given file
/// Returns a stream with data from the given file in archive.
/// </summary>
/// <param name="file">The file to read the data from</param>
/// <returns>A stream with data from the given file</returns>
System.IO.Stream OpenRead(string file);
/// <summary>
/// Creates a file in the archive
/// </summary>
/// <param name="file">The file to create</param>
/// <param name="hint">A hint to the compressor as to how compressible the file data is</param>
/// <param name="lastWrite">The time the file was last written</param>
/// <returns>A stream with the data to write into the file</returns>
System.IO.Stream CreateFile(string file, CompressionHint hint, DateTime lastWrite);
/// <summary>
/// Returns a value indicating if the specified file exists
/// </summary>
/// <param name="file">The name of the file to examine</param>
/// <returns>True if the file exists, false otherwise</returns>
bool FileExists(string file);
/// <summary>
/// The total size of the archive
/// </summary>
long Size { get; }
/// <summary>
/// Returns the last modification time for the file
/// </summary>
@@ -122,10 +105,48 @@ namespace Duplicati.Library.Interface
/// <returns>The timestamp on the file</returns>
DateTime GetLastWriteTime(string file);
/// <summary>
/// Returns a value indicating if the specified file exists
/// </summary>
/// <param name="file">The name of the file to examine</param>
/// <returns>True if the file exists, false otherwise</returns>
bool FileExists(string file);
}
public interface IArchiveWriter
{
/// <summary>
/// Creates a file in the archive.
/// </summary>
/// <param name="file">The file to create in the archive</param>
/// <param name="hint">A hint to the compressor as to how compressible the file data is</param>
/// <param name="lastWrite">The time the file was last written</param>
/// <returns>A stream with the data to write into the file</returns>
System.IO.Stream CreateFile(string file, CompressionHint hint, DateTime lastWrite);
/// <summary>
/// The size in bytes of the buffer that will be written when flushed
/// </summary>
long FlushBufferSize { get; }
}
/// <summary>
/// An interface for accessing files in an archive, such as a folder or compressed file.
/// All modules that implements compression must implement this interface.
/// The classes that implements this interface MUST also
/// implement a default constructor and a construtor that
/// has the signature new(string file, Dictionary&lt;string, string&gt; options).
/// The default constructor is used to construct an instance
/// so the DisplayName and other values can be read.
/// The other constructor is used to do the actual work.
/// The input file may not exist or have zero length, in which case it should be created.
/// </summary>
public interface ICompression : IDisposable, IArchiveReader, IArchiveWriter
{
/// <summary>
/// The total size of the archive.
/// </summary>
long Size { get; }
/// <summary>
/// The extension that the compression implementation adds to the filename
@@ -32,7 +32,6 @@ namespace Duplicati.Library.Interface
/// <summary>
/// Called when the operation finishes
/// </summary>
/// <param name="operationname">The full name of the operation</param>
/// <param name="result">The result object, if this derives from an exception, the operation failed</param>
void OnFinish(object result);
}
+1 -1
View File
@@ -62,7 +62,7 @@ namespace Duplicati.Library.Logging
m_stream.WriteLine("{0:u} - {1}: {2}", DateTime.UtcNow.ToString("u"), type, message);
if (exception != null)
{
m_stream.WriteLine(exception.ToString());
m_stream.WriteLine(exception);
m_stream.WriteLine();
}
}
+83 -132
View File
@@ -13,7 +13,7 @@ namespace Duplicati.Library.Main
internal class BackendManager : IDisposable
{
public const string VOLUME_HASH = "SHA256";
/// <summary>
/// Class to represent hash failures
/// </summary>
@@ -38,7 +38,7 @@ namespace Duplicati.Library.Main
/// <param name="innerException">The exception that caused this exception</param>
public HashMismatchException(string message, Exception innerException) : base(message, innerException) { }
}
private enum OperationType
{
Get,
@@ -162,20 +162,20 @@ namespace Duplicati.Library.Main
this.WaitForComplete();
if (Exception != null)
throw Exception;
return (TempFile)this.Result;
}
TempFile IDownloadWaitHandle.Wait(out string hash, out long size)
{
this.WaitForComplete();
if (Exception != null)
throw Exception;
hash = this.Hash;
size = this.Size;
return (TempFile)this.Result;
}
@@ -192,7 +192,7 @@ namespace Duplicati.Library.Main
this.Encrypted = true;
}
}
public bool UpdateHashAndSize(Options options)
{
if (Hash == null || Size < 0)
@@ -204,7 +204,7 @@ namespace Duplicati.Library.Main
return false;
}
public void DeleteLocalFile(IBackendWriter stat)
{
if (this.LocalTempfile != null)
@@ -212,9 +212,9 @@ namespace Duplicati.Library.Main
catch (Exception ex) { stat.AddWarning(string.Format("Failed to dispose temporary file: {0}", this.LocalTempfile), ex); }
finally { this.LocalTempfile = null; }
}
public BackendActionType BackendActionType
{
{
get
{
switch (this.Operation)
@@ -234,56 +234,7 @@ namespace Duplicati.Library.Main
}
}
}
}
/// <summary> A small utility stream that allows to keep streams open and counts the bytes sent through. </summary>
private class ShaderStream : System.IO.Stream
{
private readonly System.IO.Stream m_baseStream;
private readonly bool m_keepBaseOpen;
private long m_read = 0;
private long m_written = 0;
public ShaderStream(System.IO.Stream baseStream, bool keepBaseOpen)
{
if (baseStream == null) throw new ArgumentNullException("baseStream");
this.m_baseStream = baseStream;
this.m_keepBaseOpen = keepBaseOpen;
}
public long TotalBytesRead { get { return m_read; } }
public long TotalBytesWritten { get { return m_written; } }
public override bool CanRead { get { return m_baseStream.CanRead; } }
public override bool CanSeek { get { return m_baseStream.CanSeek; } }
public override bool CanWrite { get { return m_baseStream.CanWrite; } }
public override long Length { get { return m_baseStream.Length; } }
public override long Position
{
get { return m_baseStream.Position; }
set { m_baseStream.Position = value; }
}
public override void Flush() { m_baseStream.Flush(); }
public override long Seek(long offset, System.IO.SeekOrigin origin) { return m_baseStream.Seek(offset, origin); }
public override void SetLength(long value) { m_baseStream.SetLength(value); }
public override int Read(byte[] buffer, int offset, int count)
{
int r = m_baseStream.Read(buffer, offset, count);
m_read += r;
return r;
}
public override void Write(byte[] buffer, int offset, int count)
{
m_baseStream.Write(buffer, offset, count);
m_written += count;
}
protected override void Dispose(bool disposing)
{
if (disposing && !m_keepBaseOpen)
m_baseStream.Close();
base.Dispose(disposing);
}
}
private class DatabaseCollector
@@ -293,16 +244,16 @@ namespace Duplicati.Library.Main
private System.Threading.Thread m_callerThread;
private List<IDbEntry> m_dbqueue;
private IBackendWriter m_stats;
private interface IDbEntry { }
private class DbOperation : IDbEntry
{
public string Action;
public string File;
public string Result;
}
private class DbUpdate : IDbEntry
{
public string Remotename;
@@ -310,53 +261,53 @@ namespace Duplicati.Library.Main
public long Size;
public string Hash;
}
private class DbRename : IDbEntry
{
public string Oldname;
public string Newname;
}
public DatabaseCollector(LocalDatabase database, IBackendWriter stats)
{
m_database = database;
m_stats = stats;
m_dbqueue = new List<IDbEntry>();
m_dbqueue = new List<IDbEntry>();
if (m_database != null)
m_callerThread = System.Threading.Thread.CurrentThread;
}
public void LogDbOperation(string action, string file, string result)
{
lock(m_dbqueuelock)
lock (m_dbqueuelock)
m_dbqueue.Add(new DbOperation() { Action = action, File = file, Result = result });
}
public void LogDbUpdate(string remotename, RemoteVolumeState state, long size, string hash)
{
lock(m_dbqueuelock)
lock (m_dbqueuelock)
m_dbqueue.Add(new DbUpdate() { Remotename = remotename, State = state, Size = size, Hash = hash });
}
public void LogDbRename(string oldname, string newname)
{
lock(m_dbqueuelock)
lock (m_dbqueuelock)
m_dbqueue.Add(new DbRename() { Oldname = oldname, Newname = newname });
}
public bool FlushDbMessages(bool checkThread = false)
{
if (m_database != null && (checkThread == false || m_callerThread == System.Threading.Thread.CurrentThread))
return FlushDbMessages(m_database, null);
return false;
}
public bool FlushDbMessages(LocalDatabase db, System.Data.IDbTransaction transaction)
{
List<IDbEntry> entries;
lock(m_dbqueuelock)
lock (m_dbqueuelock)
if (m_dbqueue.Count == 0)
return false;
else
@@ -369,7 +320,7 @@ namespace Duplicati.Library.Main
HashSet<string> volsRemoved = new HashSet<string>();
//As we replace the list, we can now freely access the elements without locking
foreach(var e in entries)
foreach (var e in entries)
if (e is DbOperation)
db.LogRemoteOperation(((DbOperation)e).Action, ((DbOperation)e).File, ((DbOperation)e).Result, transaction);
else if (e is DbUpdate && ((DbUpdate)e).State == RemoteVolumeState.Deleted)
@@ -382,7 +333,7 @@ namespace Duplicati.Library.Main
else if (e is DbRename)
db.RenameRemoteFile(((DbRename)e).Oldname, ((DbRename)e).Newname, transaction);
else if (e != null)
m_stats.AddError(string.Format("Queue had element of type: {0}, {1}", e.GetType(), e.ToString()), null);
m_stats.AddError(string.Format("Queue had element of type: {0}, {1}", e.GetType(), e), null);
// Finally remove volumes from DB.
if (volsRemoved.Count > 0)
@@ -407,9 +358,9 @@ namespace Duplicati.Library.Main
// Cache these
private readonly int m_numberofretries;
private readonly TimeSpan m_retrydelay;
public string BackendUrl { get { return m_backendurl; } }
public bool HasDied { get { return m_lastException != null; } }
public Exception LastException { get { return m_lastException; } }
@@ -444,7 +395,7 @@ namespace Duplicati.Library.Main
}
if (m_taskControl != null)
m_taskControl.StateChangedEvent += (state) => {
m_taskControl.StateChangedEvent += (state) => {
if (state == TaskControlState.Abort)
m_thread.Abort();
};
@@ -509,7 +460,7 @@ namespace Duplicati.Library.Main
{
if (m_taskControl != null)
m_taskControl.TaskControlRendevouz();
if (m_options.NoConnectionReuse && m_backend != null)
{
m_backend.Dispose();
@@ -521,7 +472,7 @@ namespace Duplicati.Library.Main
if (m_backend == null)
throw new Exception("Backend failed to re-load");
using(new Logging.Timer(string.Format("RemoteOperation{0}", item.Operation)))
using (new Logging.Timer(string.Format("RemoteOperation{0}", item.Operation)))
switch (item.Operation)
{
case OperationType.Put:
@@ -558,7 +509,7 @@ namespace Duplicati.Library.Main
retries++;
lastException = ex;
m_statwriter.AddRetryAttempt(string.Format("Operation {0} with file {1} attempt {2} of {3} failed with message: {4}", item.Operation, item.RemoteFilename, retries, m_numberofretries, ex.Message), ex);
// If the thread is aborted, we exit here
if (ex is System.Threading.ThreadAbortException)
{
@@ -567,33 +518,33 @@ namespace Duplicati.Library.Main
item.SignalComplete();
throw;
}
m_statwriter.SendEvent(item.BackendActionType, retries < m_numberofretries ? BackendEventType.Retrying : BackendEventType.Failed, item.RemoteFilename, item.Size);
bool recovered = false;
if (!uploadSuccess && ex is Duplicati.Library.Interface.FolderMissingException && m_options.AutocreateFolders)
{
try
{
try
{
// If we successfully create the folder, we can re-use the connection
m_backend.CreateFolder();
m_backend.CreateFolder();
recovered = true;
}
catch(Exception dex)
{
m_statwriter.AddWarning(string.Format("Failed to create folder: {0}", ex.Message), dex);
catch (Exception dex)
{
m_statwriter.AddWarning(string.Format("Failed to create folder: {0}", ex.Message), dex);
}
}
// To work around the Apache WEBDAV issue, we rename the file here
if (item.Operation == OperationType.Put && retries < m_numberofretries && !item.NotTrackedInDb)
RenameFileAfterError(item);
if (!recovered)
{
try { m_backend.Dispose(); }
catch(Exception dex) { m_statwriter.AddWarning(LC.L("Failed to dispose backend instance: {0}", ex.Message), dex); }
catch (Exception dex) { m_statwriter.AddWarning(LC.L("Failed to dispose backend instance: {0}", ex.Message), dex); }
m_backend = null;
if (retries < m_numberofretries && m_retrydelay.Ticks != 0)
@@ -603,13 +554,13 @@ namespace Duplicati.Library.Main
{
if (m_taskControl != null && m_taskControl.IsAbortRequested())
break;
System.Threading.Thread.Sleep(500);
}
}
}
}
} while (retries < m_numberofretries);
@@ -624,7 +575,7 @@ namespace Duplicati.Library.Main
m_statwriter.AddMessage(LC.L("Recovered from problem with attempting to delete non-existing file {0}", item.RemoteFilename));
}
}
catch(Exception ex)
catch (Exception ex)
{
m_statwriter.AddWarning(LC.L("Failed to recover from error deleting file {0}", item.RemoteFilename), ex);
}
@@ -635,17 +586,17 @@ namespace Duplicati.Library.Main
item.Exception = lastException;
if (item.Operation == OperationType.Put)
item.DeleteLocalFile(m_statwriter);
if (item.ExceptionKillsHandler)
{
m_lastException = lastException;
//TODO: If there are temp files in the queue, we must delete them
m_queue.SetCompleted();
}
}
}
item.SignalComplete();
}
}
@@ -663,13 +614,13 @@ namespace Duplicati.Library.Main
var time = p.Time.Ticks == 0 ? p.Time : p.Time.AddSeconds(1);
var newname = VolumeBase.GenerateFilename(p.FileType, p.Prefix, guid, time, p.CompressionModule, p.EncryptionModule);
var oldname = item.RemoteFilename;
m_statwriter.SendEvent(item.BackendActionType, BackendEventType.Rename, oldname, item.Size);
m_statwriter.SendEvent(item.BackendActionType, BackendEventType.Rename, newname, item.Size);
m_statwriter.AddMessage(string.Format("Renaming \"{0}\" to \"{1}\"", oldname, newname));
m_db.LogDbRename(oldname, newname);
item.RemoteFilename = newname;
// If there is an index file attached to the block file,
// it references the block filename, so we create a new index file
// which is a copy of the current, but with the new name
@@ -681,13 +632,13 @@ namespace Duplicati.Library.Main
item.Indexfile.Item1.Close();
item.IndexfileUpdated = true;
}
IndexVolumeWriter wr = null;
try
{
var hashsize = HashAlgorithmHelper.Create(m_options.BlockHashAlgorithm).HashSize / 8;
wr = new IndexVolumeWriter(m_options);
using(var rd = new IndexVolumeReader(p.CompressionModule, item.Indexfile.Item2.LocalFilename, m_options, hashsize))
using (var rd = new IndexVolumeReader(p.CompressionModule, item.Indexfile.Item2.LocalFilename, m_options, hashsize))
wr.CopyFrom(rd, x => x == oldname ? newname : x);
item.Indexfile.Item1.Dispose();
item.Indexfile = new Tuple<IndexVolumeWriter, FileEntryItem>(wr, item.Indexfile.Item2);
@@ -701,7 +652,7 @@ namespace Duplicati.Library.Main
try { wr.Dispose(); }
catch { }
finally { wr = null; }
throw;
}
}
@@ -739,9 +690,9 @@ namespace Duplicati.Library.Main
private void DoPut(FileEntryItem item)
{
if (m_encryption != null)
lock(m_encryptionLock)
lock (m_encryptionLock)
item.Encrypt(m_encryption, m_statwriter);
if (item.UpdateHashAndSize(m_options) && !item.NotTrackedInDb)
m_db.LogDbUpdate(item.RemoteFilename, RemoteVolumeState.Uploading, item.Size, item.Hash);
@@ -750,7 +701,7 @@ namespace Duplicati.Library.Main
item.Indexfile.Item1.FinishVolume(item.Hash, item.Size);
item.Indexfile.Item1.Close();
item.IndexfileUpdated = true;
}
}
m_db.LogDbOperation("put", item.RemoteFilename, JsonConvert.SerializeObject(new { Size = item.Size, Hash = item.Hash }));
m_statwriter.SendEvent(BackendActionType.Put, BackendEventType.Started, item.RemoteFilename, item.Size);
@@ -772,7 +723,7 @@ namespace Duplicati.Library.Main
if (!item.NotTrackedInDb)
m_db.LogDbUpdate(item.RemoteFilename, RemoteVolumeState.Uploaded, item.Size, item.Hash);
m_statwriter.SendEvent(BackendActionType.Put, BackendEventType.Completed, item.RemoteFilename, item.Size);
if (m_options.ListVerifyUploads)
@@ -1079,7 +1030,7 @@ namespace Duplicati.Library.Main
item.Result = tmpfile;
tmpfile = null;
}
}
catch
{
@@ -1147,7 +1098,7 @@ namespace Duplicati.Library.Main
m_statwriter.AddMessage(LC.L("Listing indicates file {0} is deleted correctly", item.RemoteFilename));
return;
}
}
result = ex.ToString();
@@ -1157,11 +1108,11 @@ namespace Duplicati.Library.Main
{
m_db.LogDbOperation("delete", item.RemoteFilename, result);
}
m_db.LogDbUpdate(item.RemoteFilename, RemoteVolumeState.Deleted, -1, null);
m_statwriter.SendEvent(BackendActionType.Delete, BackendEventType.Completed, item.RemoteFilename, item.Size);
}
private void DoCreateFolder(FileEntryItem item)
{
m_statwriter.SendEvent(BackendActionType.CreateFolder, BackendEventType.Started, null, -1);
@@ -1170,7 +1121,7 @@ namespace Duplicati.Library.Main
try
{
m_backend.CreateFolder();
}
}
catch (Exception ex)
{
result = ex.ToString();
@@ -1180,15 +1131,15 @@ namespace Duplicati.Library.Main
{
m_db.LogDbOperation("createfolder", item.RemoteFilename, result);
}
m_statwriter.SendEvent(BackendActionType.CreateFolder, BackendEventType.Completed, null, -1);
}
public void PutUnencrypted(string remotename, string localpath)
{
if (m_lastException != null)
throw m_lastException;
var req = new FileEntryItem(OperationType.Put, remotename, null);
req.SetLocalfilename(localpath);
req.Encrypted = true; //Prevent encryption
@@ -1208,7 +1159,7 @@ namespace Duplicati.Library.Main
{
m_statwriter.BackendProgressUpdater.SetBlocking(false);
}
if (m_lastException != null)
throw m_lastException;
}
@@ -1217,23 +1168,23 @@ namespace Duplicati.Library.Main
{
if (m_lastException != null)
throw m_lastException;
item.Close();
var req = new FileEntryItem(OperationType.Put, item.RemoteFilename, null);
req.LocalTempfile = item.TempFile;
if (m_lastException != null)
throw m_lastException;
FileEntryItem req2 = null;
// As the network link is the bottleneck,
// we encrypt the dblock volume before the
// upload is enqueued (i.e. on the worker thread)
if (m_encryption != null)
lock (m_encryptionLock)
req.Encrypt(m_encryption, m_statwriter);
req.UpdateHashAndSize(m_options);
m_db.LogDbUpdate(item.RemoteFilename, RemoteVolumeState.Uploading, req.Size, req.Hash);
@@ -1270,7 +1221,7 @@ namespace Duplicati.Library.Main
{
m_statwriter.BackendProgressUpdater.SetBlocking(false);
}
if (m_lastException != null)
throw m_lastException;
}
@@ -1344,12 +1295,12 @@ namespace Duplicati.Library.Main
else
throw new InvalidOperationException("GetAsync called after backend is shut down");
}
public void GetForTesting(string remotename, long size, string hash)
{
if (m_lastException != null)
throw m_lastException;
if (string.IsNullOrWhiteSpace(hash))
throw new InvalidOperationException("Cannot test a file without the hash");
@@ -1372,7 +1323,7 @@ namespace Duplicati.Library.Main
if (m_lastException != null)
throw m_lastException;
}
}
public IList<Library.Interface.IFileEntry> List()
{
@@ -1477,7 +1428,7 @@ namespace Duplicati.Library.Main
{
if (m_lastException != null)
throw m_lastException;
m_db.LogDbUpdate(remotename, RemoteVolumeState.Deleting, size, null);
var req = new FileEntryItem(OperationType.Delete, remotename, size, null);
try
@@ -1498,12 +1449,12 @@ namespace Duplicati.Library.Main
if (m_lastException != null)
throw m_lastException;
}
public bool FlushDbMessages(LocalDatabase database, System.Data.IDbTransaction transaction)
{
return m_db.FlushDbMessages(database, transaction);
}
public bool FlushDbMessages()
{
return m_db.FlushDbMessages(false);
@@ -1513,7 +1464,7 @@ namespace Duplicati.Library.Main
{
if (m_queue != null && !m_queue.Completed)
m_queue.SetCompleted();
if (m_thread != null)
{
if (!m_thread.Join(TimeSpan.FromSeconds(10)))
@@ -1524,7 +1475,7 @@ namespace Duplicati.Library.Main
m_thread = null;
}
//TODO: We cannot null this, because it will be recreated
//Should we wait for queue completion or abort immediately?
if (m_backend != null)
@@ -1532,7 +1483,7 @@ namespace Duplicati.Library.Main
m_backend.Dispose();
m_backend = null;
}
try { m_db.FlushDbMessages(true); }
catch (Exception ex) { m_statwriter.AddError(string.Format("Backend Shutdown error: {0}", ex.Message), ex); }
}
+1 -1
View File
@@ -205,7 +205,7 @@ namespace Duplicati.Library.Main
{
List<string> expandedSources = new List<string>();
if (Library.Utility.Utility.IsClientWindows && (inputsources[i].StartsWith("*:") || inputsources[i].StartsWith("?:")))
if (Library.Utility.Utility.IsClientWindows && (inputsources[i].StartsWith("*:", StringComparison.Ordinal) || inputsources[i].StartsWith("?:", StringComparison.Ordinal)))
{
// *: drive paths are only supported on Windows clients
// Lazily load the drive info
@@ -275,7 +275,6 @@ namespace Duplicati.Library.Main.Database
/// Adds a block to the local database, returning a value indicating if the value presents a new block
/// </summary>
/// <param name="key">The block key</param>
/// <param name="archivename">The name of the archive that holds the data</param>
/// <returns>True if the block should be added to the current output</returns>
public bool AddBlock (string key, long size, long volumeid, System.Data.IDbTransaction transaction = null)
{
@@ -319,8 +318,6 @@ namespace Duplicati.Library.Main.Database
/// </summary>
/// <param name="filehash">The hash of the blockset</param>
/// <param name="size">The size of the blockset</param>
/// <param name="fragmentoffset">The fragmentoffset for the last block</param>
/// <param name="fragmenthash">The hash of the fragment</param>
/// <param name="hashes">The list of hashes</param>
/// <param name="blocksetid">The id of the blockset, new or old</param>
/// <returns>True if the blockset was created, false otherwise</returns>
@@ -396,7 +393,7 @@ namespace Duplicati.Library.Main.Database
/// <summary>
/// Adds a metadata set to the database, and returns a value indicating if the record was new
/// </summary>
/// <param name="hash">The metadata hash</param>
/// <param name="filehash">The metadata hash</param>
/// <param name="metadataid">The id of the metadata set</param>
/// <returns>True if the set was added to the database, false otherwise</returns>
public bool AddMetadataset(string filehash, long size, int blocksize, IEnumerable<string> blockhashes, IEnumerable<string> blocklisthashes, out long metadataid, System.Data.IDbTransaction transaction = null)
@@ -436,7 +433,6 @@ namespace Duplicati.Library.Main.Database
/// <param name="blocksetID">The ID of the hashkey for the file</param>
/// <param name="metadataID">The ID for the metadata</param>
/// <param name="transaction">The transaction to use for insertion, or null for no transaction</param>
/// <param name="operationId">The operationId to use, or -1 to use the current operation</param>
public void AddFile(string filename, DateTime lastmodified, long blocksetID, long metadataID, System.Data.IDbTransaction transaction)
{
var fileidobj = -1L;
@@ -63,8 +63,6 @@ namespace Duplicati.Library.Main.Database
/// <summary>
/// Creates a new database instance and starts a new operation
/// </summary>
/// <param name="path">The path to the database</param>
/// <param name="operation">The name of the operation</param>
public LocalDatabase(LocalDatabase db)
: this(db.m_connection)
{
@@ -77,7 +75,6 @@ namespace Duplicati.Library.Main.Database
/// <summary>
/// Creates a new database instance and starts a new operation
/// </summary>
/// <param name="path">The path to the database</param>
/// <param name="operation">The name of the operation</param>
public LocalDatabase(System.Data.IDbConnection connection, string operation)
: this(connection)
@@ -1198,7 +1195,7 @@ ORDER BY
{
cmd.Transaction = tr.Parent;
cmd.ExecuteNonQuery(string.Format(@"CREATE TEMPORARY TABLE ""{0}"" (""Path"" TEXT NOT NULL)", Tablename));
cmd.ExecuteNonQuery(string.Format(@"INSERT INTO ""{0}"" SELECT DISTINCT ""Path"" FROM ""File"" WHERE " + sb.ToString(), Tablename), args.ToArray());
cmd.ExecuteNonQuery(string.Format(@"INSERT INTO ""{0}"" SELECT DISTINCT ""Path"" FROM ""File"" WHERE " + sb, Tablename), args.ToArray());
tr.Commit();
}
}
@@ -304,7 +304,6 @@ namespace Duplicati.Library.Main.Database
/// <summary>
/// Builds a lookup table to enable faster response to block queries
/// </summary>
/// <param name="volumename">The name of the volume to prepare for</param>
public IBlockQuery CreateBlockQueryHelper(Options options, System.Data.IDbTransaction transaction)
{
return new BlockQuery(m_connection, options, transaction);
@@ -201,7 +201,7 @@ namespace Duplicati.Library.Main.Database
while (rd.Read())
{
var s = rd.GetString(0);
if (!s.StartsWith(prefix))
if (!s.StartsWith(prefix, StringComparison.Ordinal))
continue;
var dirsep = Duplicati.Library.Utility.Utility.GuessDirSeparator(s);
@@ -266,8 +266,8 @@ namespace Duplicati.Library.Main.Database
//Then we select the matching results
var filesets = string.Format(@"SELECT ""FilesetID"", ""Timestamp"" FROM ""{0}"" ORDER BY ""Timestamp"" DESC", m_tablename);
var cartesianPathFileset = string.Format(@"SELECT ""A"".""Path"", ""B"".""FilesetID"" FROM ""{0}"" A, (" + filesets + @") B ORDER BY ""A"".""Path"" ASC, ""B"".""Timestamp"" DESC", tbname, m_tablename);
var filesWithSizes = @"SELECT ""Length"", ""FilesetEntry"".""FilesetID"", ""File"".""Path"" FROM ""Blockset"", ""FilesetEntry"", ""File"" WHERE ""File"".""BlocksetID"" = ""Blockset"".""ID"" AND ""FilesetEntry"".""FileID"" = ""File"".""ID"" ";
var filesWithSizes = string.Format(@"SELECT ""Length"", ""FilesetEntry"".""FilesetID"", ""File"".""Path"" FROM ""Blockset"", ""FilesetEntry"", ""File"" WHERE ""File"".""BlocksetID"" = ""Blockset"".""ID"" AND ""FilesetEntry"".""FileID"" = ""File"".""ID"" AND FilesetEntry.""FilesetID"" IN (SELECT DISTINCT ""FilesetID"" FROM ""{0}"") ", m_tablename);
var query = @"SELECT ""C"".""Path"", ""D"".""Length"", ""C"".""FilesetID"" FROM (" + cartesianPathFileset + @") C LEFT OUTER JOIN (" + filesWithSizes + @") D ON ""C"".""FilesetID"" = ""D"".""FilesetID"" AND ""C"".""Path"" = ""D"".""Path""";
cmd.AddParameter(pathprefix, "1");
@@ -318,7 +318,7 @@ namespace Duplicati.Library.Main.Database
//Then we select the matching results
var filesets = string.Format(@"SELECT ""FilesetID"", ""Timestamp"" FROM ""{0}"" ORDER BY ""Timestamp"" DESC", m_tablename);
var cartesianPathFileset = string.Format(@"SELECT ""A"".""Path"", ""B"".""FilesetID"" FROM ""{0}"" A, (" + filesets + @") B ORDER BY ""A"".""Path"" ASC, ""B"".""Timestamp"" DESC", tmpnames.Tablename, m_tablename);
var filesWithSizes = @"SELECT ""Length"", ""FilesetEntry"".""FilesetID"", ""File"".""Path"" FROM ""Blockset"", ""FilesetEntry"", ""File"" WHERE ""File"".""BlocksetID"" = ""Blockset"".""ID"" AND ""FilesetEntry"".""FileID"" = ""File"".""ID"" ";
var filesWithSizes = string.Format(@"SELECT ""Length"", ""FilesetEntry"".""FilesetID"", ""File"".""Path"" FROM ""Blockset"", ""FilesetEntry"", ""File"" WHERE ""File"".""BlocksetID"" = ""Blockset"".""ID"" AND ""FilesetEntry"".""FileID"" = ""File"".""ID"" AND FilesetEntry.""FilesetID"" IN (SELECT DISTINCT ""FilesetID"" FROM ""{0}"") ", m_tablename);
var query = @"SELECT ""C"".""Path"", ""D"".""Length"", ""C"".""FilesetID"" FROM (" + cartesianPathFileset + @") C LEFT OUTER JOIN (" + filesWithSizes + @") D ON ""C"".""FilesetID"" = ""D"".""FilesetID"" AND ""C"".""Path"" = ""D"".""Path""";
using(var rd = cmd.ExecuteReader(query))
if(rd.Read())
@@ -368,7 +368,7 @@ namespace Duplicati.Library.Main.Database
for(var i = 0; i < m_filesets.Length; i++)
dict[m_filesets[i].Key] = i;
var summation = @"SELECT ""A"".""FilesetID"" AS ""FilesetID"", COUNT(*) AS ""FileCount"", SUM(""C"".""Length"") AS ""FileSizes"" FROM ""FilesetEntry"" A, ""File"" B, ""Blockset"" C WHERE ""A"".""FileID"" = ""B"".""ID"" AND ""B"".""BlocksetID"" = ""C"".""ID"" GROUP BY ""A"".""FilesetID"" ";
var summation = string.Format(@"SELECT ""A"".""FilesetID"" AS ""FilesetID"", COUNT(*) AS ""FileCount"", SUM(""C"".""Length"") AS ""FileSizes"" FROM ""FilesetEntry"" A, ""File"" B, ""Blockset"" C WHERE ""A"".""FileID"" = ""B"".""ID"" AND ""B"".""BlocksetID"" = ""C"".""ID"" AND ""A"".""FilesetID"" IN (SELECT DISTINCT ""FilesetID"" FROM ""{0}"") GROUP BY ""A"".""FilesetID"" ",m_tablename);
using(var cmd = m_connection.CreateCommand())
using (var rd = cmd.ExecuteReader(string.Format(@"SELECT DISTINCT ""A"".""FilesetID"", ""B"".""FileCount"", ""B"".""FileSizes"" FROM ""{0}"" A LEFT OUTER JOIN ( " + summation + @" ) B ON ""A"".""FilesetID"" = ""B"".""FilesetID"" ORDER BY ""A"".""Timestamp"" DESC ", m_tablename)))
@@ -165,7 +165,7 @@ namespace Duplicati.Library.Main.Database
{
m_fileprogtable = null;
m_totalprogtable = null;
Console.WriteLine(ex.ToString());
Console.WriteLine(ex);
throw;
}
}
@@ -217,7 +217,6 @@ namespace Duplicati.Library.Main
return false;
var file = System.IO.Path.Combine(folder, "dbconfig.json");
List<BackendEntry> configs;
if (!System.IO.File.Exists(file))
return false;
@@ -1339,9 +1339,6 @@ namespace Duplicati.Library.Main.Operation
/// </summary>
/// <param name="filename">The name of the file to record</param>
/// <param name="lastModified">The value of the lastModified timestamp</param>
/// <param name="hashlist">The list of hashes that make up the file</param>
/// <param name="size">The size of the file</param>
/// <param name="fragmentoffset">The offset into a fragment block where the last few bytes are stored</param>
/// <param name="metadata">A lookup table with various metadata values describing the file</param>
private void AddFolderToOutput(BackendManager backend, string filename, DateTime lastModified, IMetahash metadata)
{
@@ -1354,9 +1351,6 @@ namespace Duplicati.Library.Main.Operation
/// </summary>
/// <param name="filename">The name of the file to record</param>
/// <param name="lastModified">The value of the lastModified timestamp</param>
/// <param name="hashlist">The list of hashes that make up the file</param>
/// <param name="size">The size of the file</param>
/// <param name="fragmentoffset">The offset into a fragment block where the last few bytes are stored</param>
/// <param name="metadata">A lookup table with various metadata values describing the file</param>
private void AddSymlinkToOutput(BackendManager backend, string filename, DateTime lastModified, IMetahash metadata)
{
@@ -1368,10 +1362,9 @@ namespace Duplicati.Library.Main.Operation
/// Adds a file to the output,
/// </summary>
/// <param name="filename">The name of the file to record</param>
/// <param name="lastModified">The value of the lastModified timestamp</param>
/// <param name="lastmodified">The value of the lastModified timestamp</param>
/// <param name="hashlist">The list of hashes that make up the file</param>
/// <param name="size">The size of the file</param>
/// <param name="fragmentoffset">The offset into a fragment block where the last few bytes are stored</param>
/// <param name="metadata">A lookup table with various metadata values describing the file</param>
private void AddFileToOutput(BackendManager backend, string filename, long size, DateTime lastmodified, IMetahash metadata, IEnumerable<string> hashlist, string filehash, IEnumerable<string> blocklisthashes)
{
@@ -16,6 +16,7 @@
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
using System;
using System.IO;
using Duplicati.Library.Interface;
using Duplicati.Library.Main.Database;
@@ -33,7 +34,7 @@ namespace Duplicati.Library.Main.Operation
m_options = options;
m_result = result;
}
public void Run()
{
var ext = System.IO.Path.GetExtension(m_targetpath);
@@ -41,22 +42,22 @@ namespace Duplicati.Library.Main.Operation
if (ext == "" || string.Compare(ext, 1, module, 0, module.Length, StringComparison.OrdinalIgnoreCase) != 0)
m_targetpath = m_targetpath + "." + module;
if (System.IO.File.Exists(m_targetpath))
throw new UserInformationException(string.Format("Output file already exists, not overwriting: {0}", m_targetpath));
if (!System.IO.File.Exists(m_options.Dbpath))
throw new UserInformationException(string.Format("Database file does not exist: {0}", m_options.Dbpath));
m_result.OperationProgressUpdater.UpdatePhase(OperationPhase.BugReport_Running);
m_result.OperationProgressUpdater.UpdateProgress(0);
m_result.AddMessage("Scrubbing filenames from database, this may take a while, please wait");
using(var tmp = new Library.Utility.TempFile())
using (var tmp = new Library.Utility.TempFile())
{
System.IO.File.Copy(m_options.Dbpath, tmp, true);
using(var db = new LocalBugReportDatabase(tmp))
using (var db = new LocalBugReportDatabase(tmp))
{
m_result.SetDatabase(db);
db.Fix();
@@ -65,20 +66,21 @@ namespace Duplicati.Library.Main.Operation
db.Vacuum();
}
}
using(var cm = DynamicLoader.CompressionLoader.GetModule(module, m_targetpath, m_options.RawOptions))
using (var stream = new System.IO.FileStream(m_targetpath, FileMode.Create, FileAccess.Write, FileShare.Read))
using (ICompression cm = DynamicLoader.CompressionLoader.GetModule(module, stream, Interface.ArchiveMode.Write, m_options.RawOptions))
{
using(var cs = cm.CreateFile("log-database.sqlite", Duplicati.Library.Interface.CompressionHint.Compressible, DateTime.UtcNow))
using(var fs = System.IO.File.Open(tmp, System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.ReadWrite))
using (var cs = cm.CreateFile("log-database.sqlite", Duplicati.Library.Interface.CompressionHint.Compressible, DateTime.UtcNow))
using (var fs = System.IO.File.Open(tmp, System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.ReadWrite))
Library.Utility.Utility.CopyStream(fs, cs);
using(var cs = new System.IO.StreamWriter(cm.CreateFile("system-info.txt", Duplicati.Library.Interface.CompressionHint.Compressible, DateTime.UtcNow)))
foreach(var line in SystemInfoHandler.GetSystemInfo())
using (var cs = new System.IO.StreamWriter(cm.CreateFile("system-info.txt", Duplicati.Library.Interface.CompressionHint.Compressible, DateTime.UtcNow)))
foreach (var line in SystemInfoHandler.GetSystemInfo())
cs.WriteLine(line);
}
m_result.TargetPath = m_targetpath;
}
}
}
}
}
@@ -18,7 +18,8 @@
using System;
using System.Linq;
using System.Collections.Generic;
using Duplicati.Library.Interface;
namespace Duplicati.Library.Main.Operation
{
internal class ListAffected
@@ -34,6 +35,9 @@ namespace Duplicati.Library.Main.Operation
public void Run(List<string> args, Action<Duplicati.Library.Interface.IListAffectedResults> callback = null)
{
if (!System.IO.File.Exists(m_options.Dbpath))
throw new UserInformationException(string.Format("Database file does not exist: {0}", m_options.Dbpath));
using(var db = new Database.LocalListAffectedDatabase(m_options.Dbpath))
{
m_result.SetDatabase(db);
@@ -523,7 +523,7 @@ namespace Duplicati.Library.Main.Operation
if (dryrun)
return;
var isDirTarget = path.EndsWith(DIRSEP);
var isDirTarget = path.EndsWith(DIRSEP, StringComparison.Ordinal);
var targetpath = isDirTarget ? path.Substring(0, path.Length - 1) : path;
// Make the symlink first, otherwise we cannot apply metadata to it
@@ -996,7 +996,7 @@ namespace Duplicati.Library.Main.Operation
{
//Select a new filename
var ext = m_systemIO.PathGetExtension(targetpath) ?? "";
if (!string.IsNullOrEmpty(ext) && !ext.StartsWith("."))
if (!string.IsNullOrEmpty(ext) && !ext.StartsWith(".", StringComparison.Ordinal))
ext = "." + ext;
// First we try with a simple date append, assuming that there are not many conflicts there
@@ -35,7 +35,7 @@ namespace Duplicati.Library.Main.Operation
yield return string.Format("Autoupdate urls: {0}", string.Join(";", Duplicati.Library.AutoUpdater.AutoUpdateSettings.URLs));
yield return string.Format("Update folder: {0}", Duplicati.Library.AutoUpdater.UpdaterManager.INSTALLDIR);
yield return string.Format("Base install folder: {0}", Duplicati.Library.AutoUpdater.UpdaterManager.InstalledBaseDir);
yield return string.Format("Version name: \"{0}\" ({1})", Duplicati.Library.AutoUpdater.UpdaterManager.SelfVersion.Displayname, System.Reflection.Assembly.GetExecutingAssembly().GetName().Version, System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location));
yield return string.Format("Version name: \"{0}\" ({1})", Duplicati.Library.AutoUpdater.UpdaterManager.SelfVersion.Displayname, System.Reflection.Assembly.GetExecutingAssembly().GetName().Version);
yield return string.Format("Current Version folder {0}", System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location));
yield return string.Format("OS: {0}", Environment.OSVersion);
+121 -120
View File
@@ -34,11 +34,11 @@ namespace Duplicati.Library.Main
void AddDryrunMessage(string message);
void WriteLogMessageDirect(string message, LogMessageType type, Exception ex);
}
internal interface IBackendWriter : ILogWriter
{
void AddRetryAttempt(string message, Exception ex);
long UnknownFileSize { set; }
long UnknownFileCount { set; }
long KnownFileCount { set; }
@@ -57,32 +57,32 @@ namespace Duplicati.Library.Main
/// <param name="path">Path to the resource</param>
/// <param name="size">Size of the file or progress</param>
void SendEvent(BackendActionType action, BackendEventType type, string path, long size);
/// <summary>
/// Gets the backend progress updater.
/// </summary>
/// <value>The backend progress updater.</value>
IBackendProgressUpdater BackendProgressUpdater { get; }
}
internal interface ISetCommonOptions : ILogWriter
{
bool VerboseOutput { set; }
new bool VerboseOutput { set; }
bool VerboseErrors { set; }
DateTime EndTime { get; set; }
DateTime BeginTime { set; }
void SetDatabase(LocalDatabase db);
OperationMode MainOperation { get; }
IMessageSink MessageSink { set; }
IMessageSink MessageSink { set; }
}
internal class BackendWriter : BasicResults, IBackendWriter, IBackendStatstics, IParsedBackendStatistics
{
public BackendWriter(BasicResults p) : base(p) { }
protected long m_remoteCalls = 0;
protected long m_bytesUploaded = 0;
protected long m_bytesDownloaded = 0;
@@ -91,22 +91,22 @@ namespace Duplicati.Library.Main
protected long m_filesDeleted = 0;
protected long m_foldersCreated = 0;
protected long m_retryAttemptCount = 0;
public void AddNumberOfRemoteCalls(long count)
{
System.Threading.Interlocked.Add(ref m_remoteCalls, count);
}
public void AddBytesUploaded(long count)
{
System.Threading.Interlocked.Add(ref m_bytesUploaded, count);
}
public void AddBytesDownloaded(long count)
{
System.Threading.Interlocked.Add(ref m_bytesDownloaded, count);
}
public long RemoteCalls { get { return m_remoteCalls; } }
public long BytesUploaded { get { return m_bytesUploaded; } }
public long BytesDownloaded { get { return m_bytesDownloaded; } }
@@ -125,9 +125,9 @@ namespace Duplicati.Library.Main
public long TotalQuotaSpace { get; set; }
public long FreeQuotaSpace { get; set; }
public long AssignedQuotaSpace { get; set; }
public override OperationMode MainOperation { get { return m_parent.MainOperation; } }
public void SendEvent(BackendActionType action, BackendEventType type, string path, long size)
{
if (type == BackendEventType.Started)
@@ -160,13 +160,13 @@ namespace Duplicati.Library.Main
break;
}
}
base.AddBackendEvent(action, type, path, size);
}
IBackendProgressUpdater IBackendWriter.BackendProgressUpdater { get { return base.BackendProgressUpdater; } }
IBackendProgressUpdater IBackendWriter.BackendProgressUpdater { get { return base.BackendProgressUpdater; } }
}
public interface ITaskControl
{
void Pause();
@@ -174,7 +174,7 @@ namespace Duplicati.Library.Main
void Stop();
void Abort();
}
internal enum TaskControlState
{
Run,
@@ -182,7 +182,7 @@ namespace Duplicati.Library.Main
Stop,
Abort
}
internal abstract class BasicResults : IBasicResults, ILogWriter, ISetCommonOptions, ITaskControl, Logging.ILog
{
protected class DbMessage
@@ -190,7 +190,7 @@ namespace Duplicati.Library.Main
public readonly string Type;
public readonly string Message;
public readonly Exception Exception;
public DbMessage(string type, string message, Exception ex)
{
this.Type = type;
@@ -198,28 +198,28 @@ namespace Duplicati.Library.Main
this.Exception = ex;
}
}
protected LocalDatabase m_db;
protected readonly BasicResults m_parent;
protected System.Threading.Thread m_callerThread;
protected readonly object m_lock = new object();
protected Queue<DbMessage> m_dbqueue;
private TaskControlState m_controlState = TaskControlState.Run;
private System.Threading.ManualResetEvent m_pauseEvent = new System.Threading.ManualResetEvent(true);
private bool m_verboseOutput;
private bool m_verboseErrors;
public bool VerboseOutput
{
{
get
{
{
if (m_parent != null)
return m_parent.VerboseOutput;
else
return m_verboseOutput;
}
}
set
{
if (m_parent != null)
@@ -228,15 +228,16 @@ namespace Duplicati.Library.Main
m_verboseOutput = value;
}
}
public bool VerboseErrors
{
{
get
{
{
if (m_parent != null)
return m_parent.VerboseErrors;
else
return m_verboseErrors;
}
}
set
{
if (m_parent != null)
@@ -257,24 +258,24 @@ namespace Duplicati.Library.Main
else
return ParsedResultType.Success;
}
}
}
public DateTime EndTime { get; set; }
public DateTime BeginTime { get; set; }
public TimeSpan Duration { get { return EndTime.Ticks == 0 ? new TimeSpan(0) : EndTime - BeginTime; } }
public abstract OperationMode MainOperation { get; }
protected Library.Utility.FileBackedStringList m_messages;
protected Library.Utility.FileBackedStringList m_warnings;
protected Library.Utility.FileBackedStringList m_errors;
protected Library.Utility.FileBackedStringList m_retryAttempts;
protected IMessageSink m_messageSink;
public IMessageSink MessageSink
{
get { return m_messageSink; }
set
set
{
m_messageSink = value;
if (value != null)
@@ -284,7 +285,7 @@ namespace Duplicati.Library.Main
}
}
}
protected internal IOperationProgressUpdaterAndReporter m_operationProgressUpdater;
internal IOperationProgressUpdaterAndReporter OperationProgressUpdater
{
@@ -296,7 +297,7 @@ namespace Duplicati.Library.Main
return m_operationProgressUpdater;
}
}
protected internal IBackendProgressUpdaterAndReporter m_backendProgressUpdater;
internal IBackendProgressUpdaterAndReporter BackendProgressUpdater
{
@@ -308,7 +309,7 @@ namespace Duplicati.Library.Main
return m_backendProgressUpdater;
}
}
public void SetDatabase(LocalDatabase db)
{
if (m_parent != null)
@@ -317,7 +318,7 @@ namespace Duplicati.Library.Main
}
else
{
lock(m_lock)
lock (m_lock)
{
m_db = db;
if (m_db != null)
@@ -325,14 +326,14 @@ namespace Duplicati.Library.Main
}
}
}
public void FlushLog()
{
if (m_parent != null)
m_parent.FlushLog();
else
{
lock(m_lock)
lock (m_lock)
{
while (m_dbqueue.Count > 0)
{
@@ -342,7 +343,7 @@ namespace Duplicati.Library.Main
}
}
}
private void LogDbMessage(string type, string message, Exception ex)
{
if (System.Threading.Thread.CurrentThread != m_callerThread)
@@ -366,7 +367,7 @@ namespace Duplicati.Library.Main
}
else
{
lock(Logging.Log.Lock)
lock (Logging.Log.Lock)
{
if (m_is_reporting)
return;
@@ -388,7 +389,7 @@ namespace Duplicati.Library.Main
}
}
}
}
public void AddDryrunMessage(string message)
@@ -397,7 +398,7 @@ namespace Duplicati.Library.Main
m_parent.AddDryrunMessage(message);
else
{
lock(Logging.Log.Lock)
lock (Logging.Log.Lock)
{
if (m_is_reporting)
return;
@@ -451,12 +452,12 @@ namespace Duplicati.Library.Main
}
public void AddMessage(string message)
{
{
if (m_parent != null)
m_parent.AddMessage(message);
else
{
lock(Logging.Log.Lock)
lock (Logging.Log.Lock)
{
if (m_is_reporting)
return;
@@ -471,7 +472,7 @@ namespace Duplicati.Library.Main
if (MessageSink != null)
MessageSink.MessageEvent(message);
lock(m_lock)
lock (m_lock)
if (m_db != null && !m_db.IsDisposed)
LogDbMessage("Message", message, null);
}
@@ -489,7 +490,7 @@ namespace Duplicati.Library.Main
m_parent.AddWarning(message, ex);
else
{
lock(Logging.Log.Lock)
lock (Logging.Log.Lock)
{
if (m_is_reporting)
return;
@@ -505,7 +506,7 @@ namespace Duplicati.Library.Main
if (MessageSink != null)
MessageSink.WarningEvent(message, ex);
lock(m_lock)
lock (m_lock)
if (m_db != null && !m_db.IsDisposed)
LogDbMessage("Warning", message, ex);
}
@@ -523,7 +524,7 @@ namespace Duplicati.Library.Main
m_parent.AddRetryAttempt(message, ex);
else
{
lock(Logging.Log.Lock)
lock (Logging.Log.Lock)
{
if (m_is_reporting)
return;
@@ -539,7 +540,7 @@ namespace Duplicati.Library.Main
if (MessageSink != null)
MessageSink.RetryEvent(message, ex);
lock(m_lock)
lock (m_lock)
if (m_db != null && !m_db.IsDisposed)
LogDbMessage("Retry", message, ex);
}
@@ -557,7 +558,7 @@ namespace Duplicati.Library.Main
m_parent.AddError(message, ex);
else
{
lock(Logging.Log.Lock)
lock (Logging.Log.Lock)
{
if (m_is_reporting)
return;
@@ -573,7 +574,7 @@ namespace Duplicati.Library.Main
if (MessageSink != null)
MessageSink.ErrorEvent(message, ex);
lock(m_lock)
lock (m_lock)
if (m_db != null && !m_db.IsDisposed)
LogDbMessage("Error", message, ex);
}
@@ -584,14 +585,14 @@ namespace Duplicati.Library.Main
}
}
}
public IEnumerable<string> Messages { get { return m_messages; } }
public IEnumerable<string> Warnings { get { return m_warnings; } }
public IEnumerable<string> Errors { get { return m_errors; } }
public BasicResults()
{
this.BeginTime = DateTime.UtcNow;
public BasicResults()
{
this.BeginTime = DateTime.UtcNow;
this.m_parent = null;
m_messages = new Library.Utility.FileBackedStringList();
m_warnings = new Library.Utility.FileBackedStringList();
@@ -605,27 +606,27 @@ namespace Duplicati.Library.Main
}
public BasicResults(BasicResults p)
{
this.BeginTime = DateTime.UtcNow;
{
this.BeginTime = DateTime.UtcNow;
this.m_parent = p;
}
protected IBackendStatstics m_backendStatistics;
public IBackendStatstics BackendStatistics
{
{
get
{
if (this.m_parent != null)
return this.m_parent.BackendStatistics;
return m_backendStatistics;
}
}
public IBackendWriter BackendWriter { get { return (IBackendWriter)this.BackendStatistics; } }
public event Action<TaskControlState> StateChangedEvent;
/// <summary>
/// Request that this task pauses.
/// </summary>
@@ -646,7 +647,7 @@ namespace Duplicati.Library.Main
StateChangedEvent(m_controlState);
}
}
/// <summary>
/// Request that this task resumes.
/// </summary>
@@ -667,11 +668,11 @@ namespace Duplicati.Library.Main
StateChangedEvent(m_controlState);
}
}
/// <summary>
/// Request that this task stops.
/// </summary>
public void Stop()
public void Stop()
{
if (m_parent != null)
m_parent.Stop();
@@ -688,7 +689,7 @@ namespace Duplicati.Library.Main
StateChangedEvent(m_controlState);
}
}
/// <summary>
/// Request that this task aborts.
/// </summary>
@@ -708,7 +709,7 @@ namespace Duplicati.Library.Main
StateChangedEvent(m_controlState);
}
}
/// <summary>
/// Helper method that the current running task can call to obtain the current state
/// </summary>
@@ -752,7 +753,7 @@ namespace Duplicati.Library.Main
public override string ToString()
{
return Library.Utility.Utility.PrintSerializeObject(
this,
this,
filter: (prop, item) =>
!typeof(IBackendProgressUpdater).IsAssignableFrom(prop.PropertyType) &&
!typeof(IMessageSink).IsAssignableFrom(prop.PropertyType) &&
@@ -855,11 +856,11 @@ namespace Duplicati.Library.Main
else if ((Warnings != null && Warnings.Any()) || PartialBackup)
return ParsedResultType.Warning;
else
return ParsedResultType.Success;
return ParsedResultType.Success;
}
}
}
internal class RestoreResults : BasicResults, Library.Interface.IRestoreResults
{
public long FilesRestored { get; internal set; }
@@ -870,9 +871,9 @@ namespace Duplicati.Library.Main
public long FilesDeleted { get; internal set; }
public long FoldersDeleted { get; internal set; }
public long SymlinksDeleted { get; internal set; }
public override OperationMode MainOperation { get { return OperationMode.Restore; } }
public IRecreateDatabaseResults RecreateDatabaseResults { get; internal set; }
public override ParsedResultType ParsedResult
@@ -914,7 +915,7 @@ namespace Duplicati.Library.Main
this.FileSizes = fileSizes;
}
}
internal class ListResults : BasicResults, Duplicati.Library.Interface.IListResults
{
private IEnumerable<Duplicati.Library.Interface.IListResultFileset> m_filesets;
@@ -926,10 +927,10 @@ namespace Duplicati.Library.Main
m_filesets = filesets;
m_files = files;
}
public IEnumerable<Duplicati.Library.Interface.IListResultFileset> Filesets { get { return m_filesets; } }
public IEnumerable<Duplicati.Library.Interface.IListResultFile> Files { get { return m_files; } }
public override OperationMode MainOperation { get { return OperationMode.List; } }
}
@@ -947,7 +948,7 @@ namespace Duplicati.Library.Main
m_logs = logs;
m_volumes = volumes;
}
public IEnumerable<Duplicati.Library.Interface.IListResultFileset> Filesets { get { return m_filesets; } }
public IEnumerable<Duplicati.Library.Interface.IListResultFile> Files { get { return m_files; } }
public IEnumerable<Duplicati.Library.Interface.IListResultRemoteLog> LogMessages { get { return m_logs; } }
@@ -960,23 +961,23 @@ namespace Duplicati.Library.Main
{
public IEnumerable<Tuple<long, DateTime>> DeletedSets { get; private set; }
public bool Dryrun { get; private set; }
public void SetResults(IEnumerable<Tuple<long, DateTime>> deletedSets, bool dryrun)
{
EndTime = DateTime.UtcNow;
DeletedSets = deletedSets;
Dryrun = dryrun;
}
public override OperationMode MainOperation { get { return OperationMode.Delete; } }
public DeleteResults() : base() { }
public DeleteResults(BasicResults p) : base(p) { }
private ICompactResults m_compactResults;
public ICompactResults CompactResults
{
{
get
{
if (m_parent != null && m_parent is BackupResults)
@@ -988,33 +989,33 @@ namespace Duplicati.Library.Main
{
if (m_parent != null && m_parent is BackupResults)
((BackupResults)m_parent).CompactResults = value;
m_compactResults = value;
}
}
}
}
internal class RecreateDatabaseResults : BasicResults, Library.Interface.IRecreateDatabaseResults
{
public override OperationMode MainOperation { get { return OperationMode.Repair; } }
public RecreateDatabaseResults() : base() { }
public RecreateDatabaseResults(BasicResults p) : base(p) { }
}
}
internal class CreateLogDatabaseResults : BasicResults, Library.Interface.ICreateLogDatabaseResults
{
public override OperationMode MainOperation { get { return OperationMode.CreateLogDb; } }
public string TargetPath { get; internal set; }
}
}
internal class RestoreControlFilesResults : BasicResults, Library.Interface.IRestoreControlFilesResults
{
public IEnumerable<string> Files { get; private set; }
public override OperationMode MainOperation { get { return OperationMode.RestoreControlfiles; } }
public void SetResult(IEnumerable<string> files) { this.Files = files; }
}
}
internal class ListRemoteResults : BasicResults, Library.Interface.IListRemoteResults
{
@@ -1027,12 +1028,12 @@ namespace Duplicati.Library.Main
internal class RepairResults : BasicResults, Library.Interface.IRepairResults
{
public override OperationMode MainOperation { get { return OperationMode.Repair; } }
public RepairResults() : base() { }
public RepairResults(BasicResults p) : base(p) { }
public Library.Interface.IRecreateDatabaseResults RecreateDatabaseResults { get; internal set; }
}
}
internal class CompactResults : BasicResults, Library.Interface.ICompactResults
{
public long DeletedFileCount { get; internal set; }
@@ -1044,22 +1045,22 @@ namespace Duplicati.Library.Main
public bool Dryrun { get; internal set; }
public override OperationMode MainOperation { get { return OperationMode.Compact; } }
public CompactResults() : base() { }
public CompactResults(BasicResults p) : base(p) { }
}
}
internal class ListChangesResults : BasicResults, Library.Interface.IListChangesResults
{
public override OperationMode MainOperation { get { return OperationMode.ListChanges; } }
public DateTime BaseVersionTimestamp { get; internal set; }
public DateTime CompareVersionTimestamp { get; internal set; }
public long BaseVersionIndex { get; internal set; }
public long CompareVersionIndex { get; internal set; }
public IEnumerable<Tuple<ListChangesChangeType, ListChangesElementType, string>> ChangeDetails { get; internal set; }
public IEnumerable<Tuple<ListChangesChangeType, ListChangesElementType, string>> ChangeDetails { get; internal set; }
public long AddedFolders { get; internal set; }
public long AddedSymlinks { get; internal set; }
public long AddedFiles { get; internal set; }
@@ -1071,13 +1072,13 @@ namespace Duplicati.Library.Main
public long ModifiedFolders { get; internal set; }
public long ModifiedSymlinks { get; internal set; }
public long ModifiedFiles { get; internal set; }
public long PreviousSize { get; internal set; }
public long CurrentSize { get; internal set; }
public long AddedSize { get; internal set; }
public long DeletedSize { get; internal set; }
public void SetResult(
DateTime baseVersionTime, long baseVersionIndex, DateTime compareVersionTime, long compareVersionIndex,
long addedFolders, long addedSymlinks, long addedFiles,
@@ -1091,38 +1092,38 @@ namespace Duplicati.Library.Main
this.BaseVersionIndex = baseVersionIndex;
this.CompareVersionTimestamp = compareVersionTime;
this.CompareVersionIndex = compareVersionIndex;
this.AddedFolders = addedFolders;
this.AddedFolders = addedFolders;
this.AddedSymlinks = addedSymlinks;
this.AddedFiles = addedFiles;
this.DeletedFolders = deletedFolders;
this.DeletedSymlinks = deletedSymlinks;
this.DeletedFiles = deletedFiles;
this.ModifiedFolders = modifiedFolders;
this.ModifiedSymlinks = modifiedSymlinks;
this.ModifiedFiles = modifiedFiles;
this.AddedSize = addedSize;
this.DeletedSize = deletedSize;
this.PreviousSize = previousSize;
this.CurrentSize = currentSize;
this.ChangeDetails = changeDetails;
}
}
internal class TestResults : BasicResults, ITestResults
{
public TestResults() : base() { }
public TestResults(BasicResults p) : base(p) { }
public override OperationMode MainOperation { get { return OperationMode.Test; } }
public IEnumerable<KeyValuePair<string, IEnumerable<KeyValuePair<TestEntryStatus, string>>>> Verifications { get { return m_verifications; } }
private List<KeyValuePair<string, IEnumerable<KeyValuePair<TestEntryStatus, string>>>> m_verifications = new List<KeyValuePair<string, IEnumerable<KeyValuePair<TestEntryStatus, string>>>>();
public KeyValuePair<string, IEnumerable<KeyValuePair<TestEntryStatus, string>>> AddResult(string volume, IEnumerable<KeyValuePair<TestEntryStatus, string>> changes)
{
var res = new KeyValuePair<string, IEnumerable<KeyValuePair<TestEntryStatus, string>>>(volume, changes);
@@ -1130,7 +1131,7 @@ namespace Duplicati.Library.Main
return res;
}
}
internal class TestFilterResults : BasicResults, ITestFilterResults
{
public long FileCount { get; set; }
+1 -1
View File
@@ -87,7 +87,7 @@ namespace Duplicati.Library.Main.Strings
public static string DisablemoduleShort { get { return LC.L(@"Disabled one or more modules"); } }
public static string EnablemoduleLong { get { return LC.L(@"Supply one or more module names, separated by commas to load them"); } }
public static string EnablemoduleShort { get { return LC.L(@"Enables one or more modules"); } }
public static string SnapshotpolicyLong { get { return LC.L(@"This setting controls the usage of snapshots, which allows Duplicati to backup files that are locked by other programs. If this is set to ""off"", Duplicati will not attempt to create a disk snapshot. Setting this to ""auto"" makes Duplicati attempt to create a snapshot, and fail silently if that was not allowed or supported. A setting of ""on"" will also make Duplicati attempt to create a snapshot, but will produce a warning message in the log if it fails. Setting it to ""required"" will make Duplicati abort the backup if the snapshot creation fails. On windows this uses the Volume Shadow Copy Services (VSS) and requires administrative privileges. On Linux this uses Logical Volume Management (LVM) and requires root privileges."); } }
public static string SnapshotpolicyLong { get { return LC.L(@"This setting controls the usage of snapshots, which allows Duplicati to backup files that are locked by other programs. If this is set to ""off"", Duplicati will not attempt to create a disk snapshot. Setting this to ""auto"" makes Duplicati attempt to create a snapshot, and fail silently if that was not allowed or supported (note that the OS may still log system warnings). A setting of ""on"" will also make Duplicati attempt to create a snapshot, but will produce a warning message in the log if it fails. Setting it to ""required"" will make Duplicati abort the backup if the snapshot creation fails. On windows this uses the Volume Shadow Copy Services (VSS) and requires administrative privileges. On Linux this uses Logical Volume Management (LVM) and requires root privileges."); } }
public static string SnapshotpolicyShort { get { return LC.L(@"Controls the use of disk snapshots"); } }
public static string AsynchronousuploadfolderLong { get { return LC.L(@"The pre-generated volumes will be placed into the temporary folder by default, this option can set a different folder for placing the temporary volumes, despite the name, this also works for synchronous runs"); } }
public static string AsynchronousuploadfolderShort { get { return LC.L(@"The path where ready volumes are placed until uploaded"); } }
@@ -1,9 +1,8 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Duplicati.Library.Interface;
using Duplicati.Library.Interface;
using Duplicati.Library.Utility;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
namespace Duplicati.Library.Main.Volumes
@@ -12,18 +11,37 @@ namespace Duplicati.Library.Main.Volumes
{
protected bool m_disposeCompression = false;
protected ICompression m_compression;
protected Stream m_stream;
private static ICompression LoadCompressor(string compressor, string file, Options options)
private static ICompression LoadCompressor(string compressor, Stream stream, Options options)
{
var tmp = DynamicLoader.CompressionLoader.GetModule(compressor, file, options.RawOptions);
var tmp = DynamicLoader.CompressionLoader.GetModule(compressor, stream, Interface.ArchiveMode.Read, options.RawOptions);
if (tmp == null)
throw new Exception(string.Format("Unable to create {0} decompressor on file {1}", compressor, file));
{
var name = "[stream]";
if (stream is FileStream)
name = ((FileStream)stream).Name;
if (stream is TempFileStream)
name = ((TempFileStream)stream).Name;
throw new Exception(string.Format("Unable to create {0} decompressor on file {1}", compressor, name));
}
return tmp;
}
public VolumeReaderBase(string compressor, string file, Options options)
: this(LoadCompressor(compressor, file, options), options)
private static ICompression LoadCompressor(string compressor, string file, Options options, out Stream stream)
{
stream = new System.IO.FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read);
return LoadCompressor(compressor, stream, options);
}
public VolumeReaderBase(string compressor, string file, Options options)
: base(options)
{
m_compression = LoadCompressor(compressor, file, options, out m_stream);
ReadManifests(options);
m_disposeCompression = true;
}
@@ -31,13 +49,18 @@ namespace Duplicati.Library.Main.Volumes
: base(options)
{
m_compression = compression;
ReadManifests(options);
}
private void ReadManifests(Options options)
{
if (!options.DontReadManifests)
{
using (var s = m_compression.OpenRead(MANIFEST_FILENAME))
{
if (s == null)
throw new InvalidManifestException("No manifest file found in volume");
using (var fs = new StreamReader(s, ENCODING))
ManifestData.VerifyManifest(fs.ReadToEnd(), m_blocksize, options.BlockHashAlgorithm, options.FileHashAlgorithm);
}
@@ -46,7 +69,8 @@ namespace Duplicati.Library.Main.Volumes
public static void UpdateOptionsFromManifest(string compressor, string file, Options options)
{
using(var c = LoadCompressor(compressor, file, options))
using (var stream = new System.IO.FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read))
using (var c = LoadCompressor(compressor, stream, options))
using (var s = c.OpenRead(MANIFEST_FILENAME))
using (var fs = new StreamReader(s, ENCODING))
{
@@ -75,17 +99,20 @@ namespace Duplicati.Library.Main.Volumes
{
m_compression.Dispose();
m_compression = null;
m_stream?.Dispose();
m_stream = null;
}
}
public static IEnumerable<string> ReadBlocklist(ICompression compression, string filename, long hashsize)
{
var buffer = new byte[hashsize];
using(var fs = compression.OpenRead(filename))
using (var fs = compression.OpenRead(filename))
{
int s;
while ((s = Library.Utility.Utility.ForceStreamRead(fs, buffer, buffer.Length)) != 0)
{
{
if (s != buffer.Length)
throw new InvalidDataException("Premature End-of-stream encountered while reading blocklist hashes");
@@ -1,9 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Duplicati.Library.Interface;
using Duplicati.Library.Utility;
using System;
using System.IO;
using Duplicati.Library.Interface;
namespace Duplicati.Library.Main.Volumes
{
@@ -11,6 +9,7 @@ namespace Duplicati.Library.Main.Volumes
{
protected ICompression m_compression;
protected Library.Utility.TempFile m_localfile;
protected Stream m_localFileStream;
protected string m_volumename;
public string LocalFilename { get { return m_localfile; } }
public string RemoteFilename { get { return m_volumename; } }
@@ -19,7 +18,7 @@ namespace Duplicati.Library.Main.Volumes
public abstract RemoteVolumeType FileType { get; }
public long VolumeID { get; set; }
public void SetRemoteFilename(string name)
{
m_volumename = name;
@@ -29,15 +28,15 @@ namespace Duplicati.Library.Main.Volumes
: this(options, DateTime.UtcNow)
{
}
public static string GenerateGuid(Options options)
{
var s = Guid.NewGuid().ToString("N");
//We can choose shorter GUIDs here
return s;
}
public void ResetRemoteFilename(Options options, DateTime timestamp)
@@ -54,14 +53,16 @@ namespace Duplicati.Library.Main.Volumes
m_localfile = new Library.Utility.TempFile();
ResetRemoteFilename(options, timestamp);
m_compression = DynamicLoader.CompressionLoader.GetModule(options.CompressionModule, m_localfile, options.RawOptions);
if(m_compression == null)
m_localFileStream = new System.IO.FileStream(m_localfile, FileMode.Create, FileAccess.Write, FileShare.Read);
m_compression = DynamicLoader.CompressionLoader.GetModule(options.CompressionModule, m_localFileStream, ArchiveMode.Write, options.RawOptions);
if (m_compression == null)
throw new UserInformationException(string.Format("Unsupported compression module: {0}", options.CompressionModule));
if ((this is IndexVolumeWriter || this is FilesetVolumeWriter) && m_compression is Library.Interface.ICompressionHinting)
((Library.Interface.ICompressionHinting)m_compression).LowOverheadMode = true;
AddManifestfile();
}
@@ -77,6 +78,10 @@ namespace Duplicati.Library.Main.Volumes
try { m_compression.Dispose(); }
finally { m_compression = null; }
if (m_localFileStream != null)
try { m_localFileStream.Dispose(); }
finally { m_localFileStream = null; }
if (m_localfile != null)
try { m_localfile.Dispose(); }
finally { m_localfile = null; }
@@ -89,16 +94,13 @@ namespace Duplicati.Library.Main.Volumes
if (m_compression != null)
try { m_compression.Dispose(); }
finally { m_compression = null; }
if (m_localFileStream != null)
try { m_localFileStream.Dispose(); }
finally { m_localFileStream = null; }
}
public long Filesize { get { return m_compression.Size + m_compression.FlushBufferSize; } }
public void SetTempFile(Library.Utility.TempFile tmpfile)
{
if (m_localfile != null)
m_localfile.Dispose();
m_localfile = tmpfile;
}
}
}
@@ -101,9 +101,9 @@ namespace Duplicati.Library.Modules.Builtin
{
var filters = filter.Split(new string[] { System.IO.Path.PathSeparator.ToString() }, StringSplitOptions.RemoveEmptyEntries);
filtersInclude = filters.Where(x => x.StartsWith("+") && Regex.IsMatch(x, m_HyperVPathGuidRegExp, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant))
filtersInclude = filters.Where(x => x.StartsWith("+", StringComparison.Ordinal) && Regex.IsMatch(x, m_HyperVPathGuidRegExp, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant))
.Select(x => Regex.Match(x.Substring(1), m_HyperVPathGuidRegExp, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant).Groups[1].Value).ToList();
filtersExclude = filters.Where(x => x.StartsWith("-") && Regex.IsMatch(x, m_HyperVPathGuidRegExp, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant))
filtersExclude = filters.Where(x => x.StartsWith("-", StringComparison.Ordinal) && Regex.IsMatch(x, m_HyperVPathGuidRegExp, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant))
.Select(x => Regex.Match(x.Substring(1), m_HyperVPathGuidRegExp, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant).Groups[1].Value).ToList();
var remainingfilters = filters.Where(x => !Regex.IsMatch(x, m_HyperVPathGuidRegExp, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant)).ToArray();
@@ -186,7 +186,7 @@ namespace Duplicati.Library.Modules.Builtin
var pathsForBackup = new List<string>(paths);
var filterhandler = new Utility.FilterExpression(
filter.Split(new string[] { System.IO.Path.PathSeparator.ToString() }, StringSplitOptions.RemoveEmptyEntries).Where(x => x.StartsWith("-")).Select(x => x.Substring(1)).ToList());
filter.Split(new string[] { System.IO.Path.PathSeparator.ToString() }, StringSplitOptions.RemoveEmptyEntries).Where(x => x.StartsWith("-", StringComparison.Ordinal)).Select(x => x.Substring(1)).ToList());
foreach (var guestForBackup in guestsForBackup)
foreach (var pathForBackup in guestForBackup.DataPaths)
@@ -101,9 +101,9 @@ namespace Duplicati.Library.Modules.Builtin
{
var filters = filter.Split(new string[] { System.IO.Path.PathSeparator.ToString() }, StringSplitOptions.RemoveEmptyEntries);
filtersInclude = filters.Where(x => x.StartsWith("+") && Regex.IsMatch(x, m_MSSQLPathDBRegExp, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant))
filtersInclude = filters.Where(x => x.StartsWith("+", StringComparison.Ordinal) && Regex.IsMatch(x, m_MSSQLPathDBRegExp, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant))
.Select(x => Regex.Match(x.Substring(1), m_MSSQLPathDBRegExp, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant).Groups[1].Value).ToList();
filtersExclude = filters.Where(x => x.StartsWith("-") && Regex.IsMatch(x, m_MSSQLPathDBRegExp, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant))
filtersExclude = filters.Where(x => x.StartsWith("-", StringComparison.Ordinal) && Regex.IsMatch(x, m_MSSQLPathDBRegExp, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant))
.Select(x => Regex.Match(x.Substring(1), m_MSSQLPathDBRegExp, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant).Groups[1].Value).ToList();
var remainingfilters = filters.Where(x => !Regex.IsMatch(x, m_MSSQLPathDBRegExp, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant)).ToArray();
@@ -183,7 +183,7 @@ namespace Duplicati.Library.Modules.Builtin
var pathsForBackup = new List<string>(paths);
var filterhandler = new Utility.FilterExpression(
filter.Split(new string[] { System.IO.Path.PathSeparator.ToString() }, StringSplitOptions.RemoveEmptyEntries).Where(x => x.StartsWith("-")).Select(x => x.Substring(1)).ToList());
filter.Split(new string[] { System.IO.Path.PathSeparator.ToString() }, StringSplitOptions.RemoveEmptyEntries).Where(x => x.StartsWith("-", StringComparison.Ordinal)).Select(x => x.Substring(1)).ToList());
foreach (var dbForBackup in dbsForBackup)
foreach (var pathForBackup in dbForBackup.DataPaths)
@@ -143,7 +143,7 @@ namespace Duplicati.Library.Modules.Builtin
sw.WriteLine("{0}: {1}", key, value);
}
else
sw.WriteLine(c.ToString());
sw.WriteLine(c);
}
}
else if (result.GetType().IsArray)
@@ -164,7 +164,7 @@ namespace Duplicati.Library.Modules.Builtin
sw.WriteLine("{0}: {1}", key, value);
}
else
sw.WriteLine(c.ToString());
sw.WriteLine(c);
}
}
else if (result is Exception)
@@ -172,7 +172,7 @@ namespace Duplicati.Library.Modules.Builtin
//No localization, must be parseable by script
Exception e = (Exception)result;
sw.WriteLine("Failed: {0}", e.Message);
sw.WriteLine("Details: {0}", e.ToString());
sw.WriteLine("Details: {0}", e);
}
else
{
@@ -252,7 +252,7 @@ namespace Duplicati.Library.Modules.Builtin
foreach(string rawline in stdout.Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries))
{
string line = rawline.Trim();
if (!line.StartsWith("--"))
if (!line.StartsWith("--", StringComparison.Ordinal))
continue; //Ingore anything that does not start with --
line = line.Substring(2);
@@ -273,7 +273,7 @@ namespace Duplicati.Library.Modules.Builtin
key = line.Substring(0, lix).Trim();
value = line.Substring(lix + 1).Trim();
if (value.Length >= 2 && value.StartsWith("\"") && value.EndsWith("\""))
if (value.Length >= 2 && value.StartsWith("\"", StringComparison.Ordinal) && value.EndsWith("\"", StringComparison.Ordinal))
value = value.Substring(1, value.Length - 2);
}
@@ -310,7 +310,7 @@ namespace Duplicati.Library.Modules.Builtin
};
agsXMPP.XmppElementHandler loginErroHandler = (sender, e) => {
ex = new Exception(string.Format("Failed to log in: {0}", e.ToString()));
ex = new Exception(string.Format("Failed to log in: {0}", e));
waitEvent.Set();
};
@@ -152,11 +152,11 @@ namespace Duplicati.Library.SQLiteHelper
{
//The resource name will be "Duplicati.GUI.Database_schema.1.Sample upgrade.sql"
//The number indicates the version that will be upgraded to
if (s.StartsWith(prefix) && !s.Equals(prefix + SCHEMA_NAME))
if (s.StartsWith(prefix, StringComparison.Ordinal) && !s.Equals(prefix + SCHEMA_NAME))
{
try
{
string version = s.Substring(prefix.Length, s.IndexOf(".", prefix.Length + 1) - prefix.Length);
string version = s.Substring(prefix.Length, s.IndexOf(".", prefix.Length + 1, StringComparison.Ordinal) - prefix.Length);
int fileversion = int.Parse(version);
string prev;
@@ -110,7 +110,7 @@ namespace Duplicati.Library.SQLiteHelper
if (!Duplicati.Library.Utility.Utility.IsMono)
{
//If we run with MS.Net we can use the mixed mode assemblies
if (Library.Utility.Utility.Is64BitProcess)
if (Environment.Is64BitProcess)
{
if (System.IO.File.Exists(System.IO.Path.Combine(System.IO.Path.Combine(basePath, "win64"), filename)))
assemblyPath = System.IO.Path.Combine(basePath, "win64");
@@ -97,7 +97,7 @@ namespace Duplicati.Library.Snapshots
List<char> drives = new List<char>("DEFGHIJKLMNOPQRSTUVWXYZ".ToCharArray());
foreach (DriveInfo di in DriveInfo.GetDrives())
{
if ((di.RootDirectory.FullName.Length == 2 && di.RootDirectory.FullName[1] == ':') || ((di.RootDirectory.FullName.Length == 3 && di.RootDirectory.FullName.EndsWith(":\\"))))
if ((di.RootDirectory.FullName.Length == 2 && di.RootDirectory.FullName[1] == ':') || ((di.RootDirectory.FullName.Length == 3 && di.RootDirectory.FullName.EndsWith(":\\", StringComparison.Ordinal))))
{
int i = drives.IndexOf(di.RootDirectory.FullName[0]);
if (i >= 0)
@@ -110,10 +110,10 @@ namespace Duplicati.Library.Snapshots
drive = drives[0].ToString() + ':';
}
while (drive.EndsWith("\\"))
while (drive.EndsWith("\\", StringComparison.Ordinal))
drive = drive.Substring(0, drive.Length - 1);
if (!drive.EndsWith(":"))
if (!drive.EndsWith(":", StringComparison.Ordinal))
throw new ArgumentException("The drive specification must end with a colon.", nameof(drive));
Win32API.DDD_Flags flags = 0;
@@ -97,7 +97,7 @@ namespace Duplicati.Library.Snapshots
/// Gets a unique hardlink target ID
/// </summary>
/// <returns>The hardlink ID</returns>
/// <param name="file">The file or folder to examine</param>
/// <param name="path">The file or folder to examine</param>
string HardlinkTargetID(string path);
}
}
+5 -5
View File
@@ -101,7 +101,7 @@ namespace Duplicati.Library.Snapshots
/// <returns>The local path</returns>
public string ConvertToLocalPath(string path)
{
if (!path.StartsWith(m_mountPoint))
if (!path.StartsWith(m_mountPoint, StringComparison.Ordinal))
throw new InvalidOperationException();
return m_tmpDir + path.Substring(m_mountPoint.Length);
@@ -114,7 +114,7 @@ namespace Duplicati.Library.Snapshots
/// <returns>The snapshot path</returns>
public string ConvertToSnapshotPath(string path)
{
if (!path.StartsWith(m_tmpDir))
if (!path.StartsWith(m_tmpDir, StringComparison.Ordinal))
throw new InvalidOperationException();
return m_mountPoint + path.Substring(m_tmpDir.Length);
@@ -261,7 +261,7 @@ namespace Duplicati.Library.Snapshots
/// <summary>
/// Constructs a new snapshot module using LVM
/// </summary>
/// <param name="folders">The list of folders to create snapshots for</param>
/// <param name="sources">The list of folders to create snapshots for</param>
/// <param name="options">A set of commandline options</param>
public LinuxSnapshot(string[] sources, Dictionary<string, string> options)
{
@@ -342,7 +342,7 @@ namespace Duplicati.Library.Snapshots
KeyValuePair<string, SnapShot>? best = null;
foreach (KeyValuePair<string, SnapShot> s in m_entries)
if (name.StartsWith(s.Key) && (best == null || s.Key.Length > best.Value.Key.Length))
if (name.StartsWith(s.Key, StringComparison.Ordinal) && (best == null || s.Key.Length > best.Value.Key.Length))
best = s;
if (best == null)
@@ -494,7 +494,7 @@ namespace Duplicati.Library.Snapshots
/// Gets a unique hardlink target ID
/// </summary>
/// <returns>The hardlink ID</returns>
/// <param name="file">The file or folder to examine</param>
/// <param name="path">The file or folder to examine</param>
public string HardlinkTargetID(string path)
{
var local = ConvertToSnapshotPath(FindSnapShotByLocalPath(path), path);
+2 -2
View File
@@ -40,7 +40,7 @@ namespace Duplicati.Library.Snapshots
/// <summary>
/// Constructs a new backup snapshot, using all the required disks
/// </summary>
/// <param name="sourcepaths">The folders that are about to be backed up</param>
/// <param name="sources">The folders that are about to be backed up</param>
public NoSnapshot(string[] sources)
: this(sources, new Dictionary<string, string>())
{
@@ -49,7 +49,7 @@ namespace Duplicati.Library.Snapshots
/// <summary>
/// Constructs a new backup snapshot, using all the required disks
/// </summary>
/// <param name="folders">The folders that are about to be backed up</param>
/// <param name="sources">The folders that are about to be backed up</param>
/// <param name="options">A set of system options</param>
public NoSnapshot(string[] sources, Dictionary<string, string> options)
{
@@ -83,7 +83,7 @@ namespace Duplicati.Library.Snapshots
/// Gets a unique hardlink target ID
/// </summary>
/// <returns>The hardlink ID</returns>
/// <param name="file">The file or folder to examine</param>
/// <param name="path">The file or folder to examine</param>
public override string HardlinkTargetID(string path)
{
path = NormalizePath(path);
@@ -167,7 +167,7 @@ namespace Duplicati.Library.Snapshots
/// Gets a unique hardlink target ID
/// </summary>
/// <returns>The hardlink ID</returns>
/// <param name="file">The file or folder to examine</param>
/// <param name="path">The file or folder to examine</param>
public override string HardlinkTargetID(string path)
{
return null;
+7 -7
View File
@@ -31,21 +31,21 @@ namespace Duplicati.Library.Snapshots
for (int i = 0; i < args.Count; i++)
{
if (args[i].StartsWith("--"))
if (args[i].StartsWith("--", StringComparison.Ordinal))
{
string key = null;
string value = null;
if (args[i].IndexOf("=") > 0)
if (args[i].IndexOf("=", StringComparison.Ordinal) > 0)
{
key = args[i].Substring(0, args[i].IndexOf("="));
value = args[i].Substring(args[i].IndexOf("=") + 1);
key = args[i].Substring(0, args[i].IndexOf("=", StringComparison.Ordinal));
value = args[i].Substring(args[i].IndexOf("=", StringComparison.Ordinal) + 1);
}
else
key = args[i];
//Skip the leading --
key = key.Substring(2).ToLower();
if (!string.IsNullOrEmpty(value) && value.Length > 1 && value.StartsWith("\"") && value.EndsWith("\""))
if (!string.IsNullOrEmpty(value) && value.Length > 1 && value.StartsWith("\"", StringComparison.Ordinal) && value.EndsWith("\"", StringComparison.Ordinal))
value = value.Substring(1, value.Length - 2);
//Last argument overwrites the current
@@ -151,7 +151,7 @@ Where <test-folder> is the folder where files will be locked/created etc");
}
catch (Exception ex)
{
Console.WriteLine(string.Format("The file {0} was locked even through snapshot, message: {1}", filename, ex.ToString()));
Console.WriteLine(string.Format("The file {0} was locked even through snapshot, message: {1}", filename, ex));
Console.WriteLine("* Test failed");
return;
}
@@ -163,7 +163,7 @@ Where <test-folder> is the folder where files will be locked/created etc");
}
catch (Exception ex)
{
Console.WriteLine(string.Format("The snapshot tester failed: {0}", ex.ToString()));
Console.WriteLine(string.Format("The snapshot tester failed: {0}", ex));
Console.WriteLine("* Test failed");
}
+1 -1
View File
@@ -196,7 +196,7 @@ namespace Duplicati.Library.Snapshots
var f = NoSnapshot.NormalizePath(file);
foreach(var x in data.Where(x => x.Key.StartsWith("unix-ext:")).Select(x => new KeyValuePair<string, byte[]>(x.Key.Substring("unix-ext:".Length), Convert.FromBase64String(x.Value))))
foreach(var x in data.Where(x => x.Key.StartsWith("unix-ext:", StringComparison.Ordinal)).Select(x => new KeyValuePair<string, byte[]>(x.Key.Substring("unix-ext:".Length), Convert.FromBase64String(x.Value))))
UnixSupport.File.SetExtendedAttribute(f, x.Key, x.Value);
if (restorePermissions && data.ContainsKey("unix:uid-gid-perm"))
@@ -33,7 +33,7 @@ namespace Duplicati.Library.Snapshots
public static bool IsPathTooLong(string path)
{
if (path.StartsWith(UNCPREFIX) || path.StartsWith(UNCPREFIX_SERVER) || path.Length > 260)
if (path.StartsWith(UNCPREFIX, StringComparison.Ordinal) || path.StartsWith(UNCPREFIX_SERVER, StringComparison.Ordinal) || path.Length > 260)
return true;
return false;
@@ -41,13 +41,13 @@ namespace Duplicati.Library.Snapshots
public static string PrefixWithUNC(string path)
{
if (path.StartsWith(UNCPREFIX_SERVER))
if (path.StartsWith(UNCPREFIX_SERVER, StringComparison.Ordinal))
return path;
if (path.StartsWith(UNCPREFIX))
if (path.StartsWith(UNCPREFIX, StringComparison.Ordinal))
return path;
if (path.StartsWith(PATHPREFIX_SERVER))
if (path.StartsWith(PATHPREFIX_SERVER, StringComparison.Ordinal))
return UNCPREFIX_SERVER + path.Remove(0, PATHPREFIX_SERVER.Length);
return UNCPREFIX + path;
@@ -55,7 +55,7 @@ namespace Duplicati.Library.Snapshots
public static string StripUNCPrefix(string path)
{
if (path.StartsWith(UNCPREFIX))
if (path.StartsWith(UNCPREFIX, StringComparison.Ordinal))
return path.Substring(UNCPREFIX.Length);
else
return path;
@@ -510,7 +510,7 @@ namespace Duplicati.Library.Snapshots
public Dictionary<string, string> GetMetadata(string path, bool isSymlink, bool followSymlink)
{
var isDirTarget = path.EndsWith(DIRSEP);
var isDirTarget = path.EndsWith(DIRSEP, StringComparison.Ordinal);
var targetpath = isDirTarget ? path.Substring(0, path.Length - 1) : path;
var dict = new Dictionary<string, string>();
@@ -532,7 +532,7 @@ namespace Duplicati.Library.Snapshots
public void SetMetadata(string path, Dictionary<string, string> data, bool restorePermissions)
{
var isDirTarget = path.EndsWith(DIRSEP);
var isDirTarget = path.EndsWith(DIRSEP, StringComparison.Ordinal);
var targetpath = isDirTarget ? path.Substring(0, path.Length - 1) : path;
System.Security.AccessControl.FileSystemSecurity rules;
+15 -15
View File
@@ -526,13 +526,13 @@ namespace Duplicati.Library.Snapshots
/// Sends the dwIoControlCode to the device specified by hDevice.
/// </summary>
/// <param name="hDevice">Safe handle to the device </param>
/// <param name="dwIoControlCode">Device IO Control Code to send</param>
/// <param name="lpInBuffer">Input buffer if required</param>
/// <param name="IoControlCode">Device IO Control Code to send</param>
/// <param name="InBuffer">Input buffer if required</param>
/// <param name="nInBufferSize">Size of input buffer</param>
/// <param name="lpOutBuffer">Output buffer if required</param>
/// <param name="OutBuffer">Output buffer if required</param>
/// <param name="nOutBufferSize">Size of output buffer</param>
/// <param name="lpBytesReturned">Number of bytes returned in output buffer</param>
/// <param name="lpOverlapped">IntPtr to an 'OVERLAPPED' structure</param>
/// <param name="pBytesReturned">Number of bytes returned in output buffer</param>
/// <param name="overlapped">IntPtr to an 'OVERLAPPED' structure</param>
/// <returns>'true' if successful, otherwise 'false'</returns>
[DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern bool DeviceIoControl(
@@ -550,13 +550,13 @@ namespace Duplicati.Library.Snapshots
/// Sends the dwIoControlCode to the device specified by hDevice.
/// </summary>
/// <param name="hDevice">Safe handle to the device </param>
/// <param name="dwIoControlCode">Device IO Control Code to send</param>
/// <param name="lpInBuffer">Input buffer if required</param>
/// <param name="IoControlCode">Device IO Control Code to send</param>
/// <param name="InBuffer">Input buffer if required</param>
/// <param name="nInBufferSize">Size of input buffer</param>
/// <param name="lpOutBuffer">Output buffer if required</param>
/// <param name="OutBuffer">Output buffer if required</param>
/// <param name="nOutBufferSize">Size of output buffer</param>
/// <param name="lpBytesReturned">Number of bytes returned in output buffer</param>
/// <param name="lpOverlapped">IntPtr to an 'OVERLAPPED' structure</param>
/// <param name="pBytesReturned">Number of bytes returned in output buffer</param>
/// <param name="overlapped">IntPtr to an 'OVERLAPPED' structure</param>
/// <returns>'true' if successful, otherwise 'false'</returns>
[DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern bool DeviceIoControl(
@@ -574,13 +574,13 @@ namespace Duplicati.Library.Snapshots
/// Sends the dwIoControlCode to the device specified by hDevice.
/// </summary>
/// <param name="hDevice">Safe handle to the device </param>
/// <param name="dwIoControlCode">Device IO Control Code to send</param>
/// <param name="lpInBuffer">Input buffer if required</param>
/// <param name="IoControlCode">Device IO Control Code to send</param>
/// <param name="InBuffer">Input buffer if required</param>
/// <param name="nInBufferSize">Size of input buffer</param>
/// <param name="lpOutBuffer">Output buffer if required</param>
/// <param name="OutBuffer">Output buffer if required</param>
/// <param name="nOutBufferSize">Size of output buffer</param>
/// <param name="lpBytesReturned">Number of bytes returned in output buffer</param>
/// <param name="lpOverlapped">IntPtr to an 'OVERLAPPED' structure</param>
/// <param name="pBytesReturned">Number of bytes returned in output buffer</param>
/// <param name="overlapped">IntPtr to an 'OVERLAPPED' structure</param>
/// <returns>'true' if successful, otherwise 'false'</returns>
[DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern bool DeviceIoControl(
@@ -281,7 +281,7 @@ namespace Duplicati.Library.Snapshots
localPath = localPath.Replace(root, String.Empty);
if (!volumePath.EndsWith(SLASH) && !localPath.StartsWith(SLASH))
if (!volumePath.EndsWith(SLASH, StringComparison.Ordinal) && !localPath.StartsWith(SLASH, StringComparison.Ordinal))
localPath = localPath.Insert(0, SLASH);
localPath = localPath.Insert(0, volumePath);
@@ -78,9 +78,9 @@ namespace Duplicati.Library.UsageReporter
if (m != null)
{
var lines = m.Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
var product = lines.Where(x => x.Trim().StartsWith("ProductName:")).Select(x => x.Trim().Substring("ProductName:".Length).Trim()).FirstOrDefault();
var version = lines.Where(x => x.Trim().StartsWith("ProductVersion:")).Select(x => x.Trim().Substring("ProductVersion:".Length).Trim()).FirstOrDefault();
var build = lines.Where(x => x.Trim().StartsWith("BuildVersion:")).Select(x => x.Trim().Substring("BuildVersion:".Length).Trim()).FirstOrDefault();
var product = lines.Where(x => x.Trim().StartsWith("ProductName:", StringComparison.Ordinal)).Select(x => x.Trim().Substring("ProductName:".Length).Trim()).FirstOrDefault();
var version = lines.Where(x => x.Trim().StartsWith("ProductVersion:", StringComparison.Ordinal)).Select(x => x.Trim().Substring("ProductVersion:".Length).Trim()).FirstOrDefault();
var build = lines.Where(x => x.Trim().StartsWith("BuildVersion:", StringComparison.Ordinal)).Select(x => x.Trim().Substring("BuildVersion:".Length).Trim()).FirstOrDefault();
if (!string.IsNullOrWhiteSpace(product))
return string.Format("{0} {1} {2}", product, version, build);
}
@@ -145,7 +145,7 @@ namespace Duplicati.Library.UsageReporter
if (m != null)
{
var lines = m.Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
var line = lines.Where(x => x.Trim().StartsWith("Description:")).Select(x => x.Trim().Substring("Description:".Length).Trim()).FirstOrDefault();
var line = lines.Where(x => x.Trim().StartsWith("Description:", StringComparison.Ordinal)).Select(x => x.Trim().Substring("Description:".Length).Trim()).FirstOrDefault();
if (!string.IsNullOrWhiteSpace(line))
return line;
}
@@ -154,7 +154,6 @@ namespace Duplicati.Library.UsageReporter
return RunProgramAndReadOutput("uname", "-srvmpio");
}
return null;
}
}
}
@@ -41,21 +41,21 @@ namespace Duplicati.Library.Utility
for (int i = 0; i < args.Count; i++)
{
if (args[i].StartsWith("--"))
if (args[i].StartsWith("--", StringComparison.Ordinal))
{
string key = null;
string value = null;
if (args[i].IndexOf("=") > 0)
if (args[i].IndexOf("=", StringComparison.Ordinal) > 0)
{
key = args[i].Substring(0, args[i].IndexOf("="));
value = args[i].Substring(args[i].IndexOf("=") + 1);
key = args[i].Substring(0, args[i].IndexOf("=", StringComparison.Ordinal));
value = args[i].Substring(args[i].IndexOf("=", StringComparison.Ordinal) + 1);
}
else
key = args[i];
//Skip the leading --
key = key.Substring(2).ToLower();
if (!string.IsNullOrEmpty(value) && value.Length > 1 && value.StartsWith("\"") && value.EndsWith("\""))
if (!string.IsNullOrEmpty(value) && value.Length > 1 && value.StartsWith("\"", StringComparison.Ordinal) && value.EndsWith("\"", StringComparison.Ordinal))
value = value.Substring(1, value.Length - 2);
//Last argument overwrites the current
+430 -426
View File
@@ -1,417 +1,421 @@
// Copyright (C) 2015, The Duplicati Team
// http://www.duplicati.com, info@duplicati.com
//
// This library is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; either version 2.1 of the
// License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
using System;
using System.Collections.Generic;
using System.Linq;
namespace Duplicati.Library.Utility
{
[Flags]
public enum DefaultFilterSet
{
None = 0,
Windows = 1,
OSX = 2,
Linux = 4,
All = Windows | OSX | Linux,
}
/// <summary>
/// This class defines a set of common filters for files that don't typically need to be backed up.
/// These filters are largely based on the filters described here:
/// https://superuser.com/questions/443890/what-files-file-types-and-folders-to-exclude-from-user-folder-backup
/// </summary>
public static class DefaultFilters
{
/// <summary>
/// Case insensitive mapping from strings to filter sets
/// /// </summary>
private static readonly Dictionary<string, DefaultFilterSet> defaultFilterSetMapping =
((DefaultFilterSet[])Enum.GetValues(typeof(DefaultFilterSet)))
.ToDictionary(val => val.ToString(), val => val, StringComparer.OrdinalIgnoreCase);
/// <summary>
/// Filters common for all operating systems
/// </summary>
public static IEnumerable<string> Common
{
get
{
return DefaultFilters.CreateCommonFilters();
}
}
/// <summary>
/// Filters for Windows
/// </summary>
public static IEnumerable<string> Windows
{
get
{
return DefaultFilters.CreateWindowsFilters();
}
}
/// <summary>
/// Filters for OSX
/// </summary>
public static IEnumerable<string> OSX
{
get
{
return DefaultFilters.CreateOSXFilters();
}
}
/// <summary>
/// Filters for Linux
/// </summary>
public static IEnumerable<string> Linux
{
get
{
return DefaultFilters.CreateLinuxFilters();
}
}
/// <summary>
/// Gets the filters indicated by the given filter sets
/// </summary>
/// <param name="options">Filter set options</param>
/// <returns>Default filters</returns>
public static IEnumerable<FilterExpression> GetFilters(string options)
{
bool anyOptions = false;
DefaultFilterSet filterSets = DefaultFilterSet.None;
foreach (string option in options.Split(new[] { ',', '|', ';' }, StringSplitOptions.RemoveEmptyEntries))
{
anyOptions = true;
DefaultFilterSet filter;
if (DefaultFilters.defaultFilterSetMapping.TryGetValue(option, out filter))
{
filterSets |= filter;
}
else
{
throw new ArgumentException(Strings.Filters.UnknownDefaultFilterSet(option));
}
}
// If no filter sets are specified, we use the default for the platform we're on
if (!anyOptions)
{
if (Utility.IsClientWindows)
{
filterSets |= DefaultFilterSet.Windows;
}
if (Utility.IsClientOSX)
{
filterSets |= DefaultFilterSet.OSX;
}
if (Utility.IsClientLinux)
{
filterSets |= DefaultFilterSet.Linux;
}
}
if (filterSets == DefaultFilterSet.None)
{
return Enumerable.Empty<FilterExpression>();
}
IEnumerable<string> filters = DefaultFilters.Common;
if ((filterSets & DefaultFilterSet.Windows) == DefaultFilterSet.Windows)
{
filters = filters.Concat(DefaultFilters.Windows);
}
if ((filterSets & DefaultFilterSet.OSX) == DefaultFilterSet.OSX)
{
filters = filters.Concat(DefaultFilters.OSX);
}
if ((filterSets & DefaultFilterSet.Linux) == DefaultFilterSet.Linux)
{
filters = filters.Concat(DefaultFilters.Linux);
}
// Filter down to distinct filters, then convert them to filter expressions
return filters.Distinct(Utility.ClientFilenameStringComparer).Select(filter => new FilterExpression(filter, false));
}
/// <summary>
/// Creates common filters
/// </summary>
/// <returns>Common filters</returns>
private static string[] CreateCommonFilters()
{
return new[]
{
DefaultFilters.CreateWildcardFilter(@"*/Google/Chrome/*cache*"),
DefaultFilters.CreateWildcardFilter(@"*/Google/Chrome/Safe Browsing*"),
DefaultFilters.CreateWildcardFilter(@"*/iPhoto Library/iPod Photo Cache/*"),
DefaultFilters.CreateWildcardFilter(@"*/Mozilla/Firefox/*cache*"),
DefaultFilters.CreateRegexFilter(@".*/(cookies|permissions).sqllite(-.{3})?"),
};
}
/// <summary>
/// Creates Windows filters
/// </summary>
/// <returns>Windows filters</returns>
private static string[] CreateWindowsFilters()
{
var filters = new[]
{
DefaultFilters.CreateWildcardFilter(@"*.rbf"),
DefaultFilters.CreateWildcardFilter(@"*.tmp"),
DefaultFilters.CreateWildcardFilter(@"*.tmp/*"),
DefaultFilters.CreateWildcardFilter(@"*/$RECYCLE.BIN/*"),
DefaultFilters.CreateWildcardFilter(@"*/AppData/Apple Computer/Mobile Sync/*"),
DefaultFilters.CreateWildcardFilter(@"*/AppData/Local/Microsoft/Windows Store/*"),
DefaultFilters.CreateWildcardFilter(@"*/AppData/Local/Packages/*"), // https://superuser.com/questions/490925/explain-windows-8-windows-store-appdata-packages-and-what-to-backup
DefaultFilters.CreateWildcardFilter(@"*/AppData/Local/Temp*"),
DefaultFilters.CreateWildcardFilter(@"*/AppData/LocalLow/*"),
DefaultFilters.CreateWildcardFilter(@"*/AppData/Temp*"),
DefaultFilters.CreateWildcardFilter(@"*/Application Data/Apple Computer/Mobile Sync/*"),
DefaultFilters.CreateWildcardFilter(@"*/Application Data/Application Data*"),
DefaultFilters.CreateWildcardFilter(@"*/Cookies/*"),
DefaultFilters.CreateWildcardFilter(@"*/Google/Chrome/User Data/Default/Cookies"),
DefaultFilters.CreateWildcardFilter(@"*/Google/Chrome/User Data/Default/Cookies-journal"),
DefaultFilters.CreateWildcardFilter(@"*/I386*"),
DefaultFilters.CreateWildcardFilter(@"*/Internet Explorer/*"),
DefaultFilters.CreateWildcardFilter(@"*/Local Settings/History/*"),
DefaultFilters.CreateWildcardFilter(@"*/Local Settings/Temp*"),
DefaultFilters.CreateWildcardFilter(@"*/LocalService/*"),
DefaultFilters.CreateWildcardFilter(@"*/Microsoft*/RecoveryStore*"),
DefaultFilters.CreateWildcardFilter(@"*/Microsoft*/Windows/*.edb"),
DefaultFilters.CreateWildcardFilter(@"*/Microsoft*/Windows/*.log"),
DefaultFilters.CreateWildcardFilter(@"*/Microsoft*/Windows/Cookies*"),
DefaultFilters.CreateWildcardFilter(@"*/MSOCache*"),
DefaultFilters.CreateWildcardFilter(@"*/NetHood/*"),
DefaultFilters.CreateWildcardFilter(@"*/NetworkService/*"),
DefaultFilters.CreateWildcardFilter(@"*/NTUSER*"),
DefaultFilters.CreateWildcardFilter(@"*/ntuser.dat*"),
DefaultFilters.CreateWildcardFilter(@"*/PrintHood/*"),
DefaultFilters.CreateWildcardFilter(@"*/Recent/*"),
DefaultFilters.CreateWildcardFilter(@"*/RECYCLER/*"),
DefaultFilters.CreateWildcardFilter(@"*/Safari/Library/Caches/*"),
DefaultFilters.CreateWildcardFilter(@"*/SendTo/*"),
DefaultFilters.CreateWildcardFilter(@"*/Start Menu/*"),
DefaultFilters.CreateWildcardFilter(@"*/Temporary Internet Files/*"),
DefaultFilters.CreateWildcardFilter(@"*/Thumbs.db"),
DefaultFilters.CreateWildcardFilter(@"*UsrClass.dat"),
DefaultFilters.CreateWildcardFilter(@"*UsrClass.dat.LOG"),
DefaultFilters.CreateWildcardFilter(@"?:/autoexec.bat"),
DefaultFilters.CreateWildcardFilter(@"?:/Config.Msi*"),
DefaultFilters.CreateWildcardFilter(@"?:/hiberfil.sys"),
DefaultFilters.CreateWildcardFilter(@"?:/pagefile.sys"),
DefaultFilters.CreateWildcardFilter(@"?:/Program Files (x86)/*"),
DefaultFilters.CreateWildcardFilter(@"?:/Program Files/*"),
DefaultFilters.CreateWildcardFilter(@"?:/ProgramData/*"),
DefaultFilters.CreateWildcardFilter(@"?:/swapfile.sys"),
DefaultFilters.CreateWildcardFilter(@"?:/System Volume Information/*"),
DefaultFilters.CreateWildcardFilter(@"?:/Windows.old/*"),
DefaultFilters.CreateWildcardFilter(@"?:/Windows/*"),
DefaultFilters.CreateWildcardFilter(@"?:/Windows/Installer*"),
DefaultFilters.CreateWildcardFilter(@"?:/Windows/Temp*"),
};
var extra = GetWindowsRegistryFilters();
if (extra == null || extra.Length == 0)
return filters;
return filters.Union(extra).ToArray();
}
/// <summary>
/// Creates OSX filters
/// </summary>
/// <returns>OSX filters</returns>
private static string[] CreateOSXFilters()
{
return new[]
{
DefaultFilters.CreateWildcardFilter(@"*.fseventsd*"),
DefaultFilters.CreateWildcardFilter(@"*.hotfiles.btree*"),
DefaultFilters.CreateWildcardFilter(@"*.Spotlight-*/*"),
DefaultFilters.CreateWildcardFilter(@"*.Trash*"),
DefaultFilters.CreateWildcardFilter(@"*/Application Support/Google/Chrome/Default/Cookies"),
DefaultFilters.CreateWildcardFilter(@"*/Application Support/Google/Chrome/Default/Cookies-journal"),
DefaultFilters.CreateWildcardFilter(@"*/backups.backupdb/*"),
DefaultFilters.CreateWildcardFilter(@"*/iP* Software Updates/*"),
DefaultFilters.CreateWildcardFilter(@"*/iPhoto Library/iPod Photo Cache*"),
DefaultFilters.CreateWildcardFilter(@"*/iTunes/Album Artwork/Cache/*"),
DefaultFilters.CreateWildcardFilter(@"*/Library/Application Support/SyncServices/*"),
DefaultFilters.CreateWildcardFilter(@"*/Library/Caches/*"),
DefaultFilters.CreateWildcardFilter(@"*/Library/Calendars/*/Info.plist"),
DefaultFilters.CreateWildcardFilter(@"*/Library/Calendars/Calendar Cache"),
DefaultFilters.CreateWildcardFilter(@"*/Library/Cookies/com.apple.appstore.plist"),
DefaultFilters.CreateWildcardFilter(@"*/Library/Cookies/Cookies.binarycookies"),
DefaultFilters.CreateWildcardFilter(@"*/Library/Logs/*"),
DefaultFilters.CreateWildcardFilter(@"*/Library/Mail/*/Info.plist"),
DefaultFilters.CreateWildcardFilter(@"*/Library/Mail/AvailableFeeds/*"),
DefaultFilters.CreateWildcardFilter(@"*/Library/Mail/Envelope Index"),
DefaultFilters.CreateWildcardFilter(@"*/Library/Mirrors/*"),
DefaultFilters.CreateWildcardFilter(@"*/Library/PubSub/Database/*"),
DefaultFilters.CreateWildcardFilter(@"*/Library/PubSub/Downloads/*"),
DefaultFilters.CreateWildcardFilter(@"*/Library/PubSub/Feeds/*"),
DefaultFilters.CreateWildcardFilter(@"*/Library/Safari/HistoryIndex.sk"),
DefaultFilters.CreateWildcardFilter(@"*/Library/Safari/Icons.db"),
DefaultFilters.CreateWildcardFilter(@"*/Library/Safari/WebpageIcons.db"),
DefaultFilters.CreateWildcardFilter(@"*/Library/Saved Application State/*"),
DefaultFilters.CreateWildcardFilter(@"*/lost+found/*"),
DefaultFilters.CreateWildcardFilter(@"*/Microsoft User Data/Entourage Temp/*"),
DefaultFilters.CreateWildcardFilter(@"*/Network Trash Folder/*"),
DefaultFilters.CreateWildcardFilter(@"*/Trash/*"),
DefaultFilters.CreateWildcardFilter(@"*/VM Storage"),
DefaultFilters.CreateWildcardFilter(@"*Mobile.*Backups/*"),
DefaultFilters.CreateWildcardFilter(@"/.vol/*"),
DefaultFilters.CreateWildcardFilter(@"/afs/*"),
DefaultFilters.CreateWildcardFilter(@"/automount/*"),
DefaultFilters.CreateWildcardFilter(@"/bin/*"),
DefaultFilters.CreateWildcardFilter(@"/cores/*"),
DefaultFilters.CreateWildcardFilter(@"/Desktop DB"),
DefaultFilters.CreateWildcardFilter(@"/Desktop DF"),
DefaultFilters.CreateWildcardFilter(@"/dev/.*"),
DefaultFilters.CreateWildcardFilter(@"/etc/*"),
DefaultFilters.CreateWildcardFilter(@"/mach.sym"),
DefaultFilters.CreateWildcardFilter(@"/mach_kernel"),
DefaultFilters.CreateWildcardFilter(@"/net/*"),
DefaultFilters.CreateWildcardFilter(@"/Network/*"),
DefaultFilters.CreateWildcardFilter(@"/Network/Servers*"),
DefaultFilters.CreateWildcardFilter(@"/Previous Systems*"),
DefaultFilters.CreateWildcardFilter(@"/private/*"),
DefaultFilters.CreateWildcardFilter(@"/private/Network/*"),
DefaultFilters.CreateWildcardFilter(@"/private/tmp/*"),
DefaultFilters.CreateWildcardFilter(@"/private/var/automount/*"),
DefaultFilters.CreateWildcardFilter(@"/private/var/db/dhcpclient/*"),
DefaultFilters.CreateWildcardFilter(@"/private/var/db/fseventsd/*"),
DefaultFilters.CreateWildcardFilter(@"/private/var/folders/*"),
DefaultFilters.CreateWildcardFilter(@"/private/var/run/*"),
DefaultFilters.CreateWildcardFilter(@"/private/var/spool/postfix/*"),
DefaultFilters.CreateWildcardFilter(@"/private/var/tmp/*"),
DefaultFilters.CreateWildcardFilter(@"/private/var/vm/*"),
DefaultFilters.CreateWildcardFilter(@"/sbin/*"),
DefaultFilters.CreateWildcardFilter(@"/sw/*"),
DefaultFilters.CreateWildcardFilter(@"/System/*"),
DefaultFilters.CreateWildcardFilter(@"/System/Library/Extensions/Caches/*"),
DefaultFilters.CreateWildcardFilter(@"/tmp/*"),
DefaultFilters.CreateWildcardFilter(@"/Users/Shared/SC Info*"),
DefaultFilters.CreateWildcardFilter(@"/usr/*"),
};
}
/// <summary>
/// Creates Linux filters
/// </summary>
/// <returns>Linux filters</returns>
private static string[] CreateLinuxFilters()
{
return new[]
{
DefaultFilters.CreateWildcardFilter(@"*/.config/google-chrome/Default/Cookies"),
DefaultFilters.CreateWildcardFilter(@"*/.config/google-chrome/Default/Cookies-journal"),
DefaultFilters.CreateWildcardFilter(@"*/lost+found/*"),
DefaultFilters.CreateWildcardFilter(@"*~"),
DefaultFilters.CreateWildcardFilter(@"/bin/*"),
DefaultFilters.CreateWildcardFilter(@"/boot/*"),
DefaultFilters.CreateWildcardFilter(@"/dev/*"),
DefaultFilters.CreateWildcardFilter(@"/etc/*"),
DefaultFilters.CreateWildcardFilter(@"/initrd/*"),
DefaultFilters.CreateWildcardFilter(@"/lib/*"),
DefaultFilters.CreateWildcardFilter(@"/opt/*"),
DefaultFilters.CreateWildcardFilter(@"/proc/*"),
DefaultFilters.CreateWildcardFilter(@"/sbin/*"),
DefaultFilters.CreateWildcardFilter(@"/selinux/*"),
DefaultFilters.CreateWildcardFilter(@"/srv/*"),
DefaultFilters.CreateWildcardFilter(@"/sys/*"),
DefaultFilters.CreateWildcardFilter(@"/tmp/*"),
DefaultFilters.CreateWildcardFilter(@"/usr/*"),
DefaultFilters.CreateWildcardFilter(@"/var/*"),
};
}
/// <summary>
/// Creates a wildcard filter
/// </summary>
/// <param name="filter">Filter text</param>
/// <returns>Wildcard filter</returns>
private static string CreateWildcardFilter(string filter)
{
// Create a filter with the given name.
// However, in order to match paths correctly, the directory separators need to be normalized to match the system default.
return filter.Replace(System.IO.Path.AltDirectorySeparatorChar, System.IO.Path.DirectorySeparatorChar);
}
/// <summary>
/// Creates a Regex filter
/// </summary>
/// <param name="filter">Filter text</param>
/// <returns>Regex filter</returns>
private static string CreateRegexFilter(string filter)
{
// Create a filter with the given name.
// However, in order to match paths correctly, the directory separators need to be normalized to match the system default.
string escapedAlt = System.Text.RegularExpressions.Regex.Escape(System.IO.Path.AltDirectorySeparatorChar.ToString());
string escaped = System.Text.RegularExpressions.Regex.Escape(System.IO.Path.DirectorySeparatorChar.ToString());
return "[" + filter.Replace(escapedAlt, escaped) + "]";
}
/// <summary>
/// Wrapper method for getting a list of exclude paths from the Windows registry.
/// This method guards the call to <see cref="GetWindowsRegistryFiltersInternal"/> to avoid loader errors.
/// </summary>
/// <returns>The list of paths to exclude.</returns>
private static string[] GetWindowsRegistryFilters()
{
try
{
return GetWindowsRegistryFiltersInternal();
}
catch
{
}
return new string[0];
}
/// <summary>
/// Helper method that reads the Windows registry and finds paths to exclude.
/// This method should not be called directly as that could cause loader errors on Mono.
/// </summary>
/// <returns>The list of paths to exclude.</returns>
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
private static string[] GetWindowsRegistryFiltersInternal()
{
var rk = Microsoft.Win32.RegistryKey.OpenBaseKey(Microsoft.Win32.RegistryHive.LocalMachine, Microsoft.Win32.RegistryView.Default);
if (rk == null)
return new string[0];
var sk = rk.OpenSubKey(@"SYSTEM\CurrentControlSet\Control\BackupRestore\FilesNotToBackup");
if (sk == null)
return new string[0];
// Each value in this key is a potential path
return sk.GetValueNames()
.Where(x => x != null)
// Copyright (C) 2015, The Duplicati Team
// http://www.duplicati.com, info@duplicati.com
//
// This library is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; either version 2.1 of the
// License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
using System;
using System.Collections.Generic;
using System.Linq;
namespace Duplicati.Library.Utility
{
[Flags]
public enum DefaultFilterSet
{
None = 0,
Windows = 1,
OSX = 2,
Linux = 4,
All = Windows | OSX | Linux,
}
/// <summary>
/// This class defines a set of common filters for files that don't typically need to be backed up.
/// These filters are largely based on the filters described here:
/// https://superuser.com/questions/443890/what-files-file-types-and-folders-to-exclude-from-user-folder-backup
/// </summary>
public static class DefaultFilters
{
/// <summary>
/// Case insensitive mapping from strings to filter sets
/// /// </summary>
private static readonly Dictionary<string, DefaultFilterSet> defaultFilterSetMapping =
((DefaultFilterSet[])Enum.GetValues(typeof(DefaultFilterSet)))
.ToDictionary(val => val.ToString(), val => val, StringComparer.OrdinalIgnoreCase);
/// <summary>
/// Filters common for all operating systems
/// </summary>
public static IEnumerable<string> Common
{
get
{
return DefaultFilters.CreateCommonFilters();
}
}
/// <summary>
/// Filters for Windows
/// </summary>
public static IEnumerable<string> Windows
{
get
{
return DefaultFilters.CreateWindowsFilters();
}
}
/// <summary>
/// Filters for OSX
/// </summary>
public static IEnumerable<string> OSX
{
get
{
return DefaultFilters.CreateOSXFilters();
}
}
/// <summary>
/// Filters for Linux
/// </summary>
public static IEnumerable<string> Linux
{
get
{
return DefaultFilters.CreateLinuxFilters();
}
}
/// <summary>
/// Gets the filters indicated by the given filter sets
/// </summary>
/// <param name="options">Filter set options</param>
/// <returns>Default filters</returns>
public static IEnumerable<FilterExpression> GetFilters(string options)
{
bool anyOptions = false;
DefaultFilterSet filterSets = DefaultFilterSet.None;
foreach (string option in options.Split(new[] { ',', '|', ';' }, StringSplitOptions.RemoveEmptyEntries))
{
anyOptions = true;
DefaultFilterSet filter;
if (DefaultFilters.defaultFilterSetMapping.TryGetValue(option, out filter))
{
filterSets |= filter;
}
else
{
throw new ArgumentException(Strings.Filters.UnknownDefaultFilterSet(option));
}
}
// If no filter sets are specified, we use the default for the platform we're on
if (!anyOptions)
{
if (Utility.IsClientWindows)
{
filterSets |= DefaultFilterSet.Windows;
}
if (Utility.IsClientOSX)
{
filterSets |= DefaultFilterSet.OSX;
}
if (Utility.IsClientLinux)
{
filterSets |= DefaultFilterSet.Linux;
}
}
if (filterSets == DefaultFilterSet.None)
{
return Enumerable.Empty<FilterExpression>();
}
IEnumerable<string> filters = DefaultFilters.Common;
if ((filterSets & DefaultFilterSet.Windows) == DefaultFilterSet.Windows)
{
filters = filters.Concat(DefaultFilters.Windows);
}
if ((filterSets & DefaultFilterSet.OSX) == DefaultFilterSet.OSX)
{
filters = filters.Concat(DefaultFilters.OSX);
}
if ((filterSets & DefaultFilterSet.Linux) == DefaultFilterSet.Linux)
{
filters = filters.Concat(DefaultFilters.Linux);
}
// Filter down to distinct filters, then convert them to filter expressions
return filters.Distinct(Utility.ClientFilenameStringComparer).Select(filter => new FilterExpression(filter, false));
}
/// <summary>
/// Creates common filters
/// </summary>
/// <returns>Common filters</returns>
private static string[] CreateCommonFilters()
{
return new[]
{
DefaultFilters.CreateWildcardFilter(@"*/Google/Chrome/*cache*"),
DefaultFilters.CreateWildcardFilter(@"*/Google/Chrome/Safe Browsing*"),
DefaultFilters.CreateWildcardFilter(@"*/iPhoto Library/iPod Photo Cache/*"),
DefaultFilters.CreateWildcardFilter(@"*/Mozilla/Firefox/*cache*"),
DefaultFilters.CreateRegexFilter(@".*/(cookies|permissions).sqllite(-.{3})?"),
};
}
/// <summary>
/// Creates Windows filters
/// </summary>
/// <returns>Windows filters</returns>
private static string[] CreateWindowsFilters()
{
var filters = new[]
{
DefaultFilters.CreateWildcardFilter(@"*/config.msi/*.rbf"), // https://github.com/duplicati/duplicati/issues/2886
DefaultFilters.CreateWildcardFilter(@"*.tmp"),
DefaultFilters.CreateWildcardFilter(@"*.tmp/*"),
DefaultFilters.CreateWildcardFilter(@"*/$RECYCLE.BIN/*"),
DefaultFilters.CreateWildcardFilter(@"*/AppData/Apple Computer/Mobile Sync/*"),
DefaultFilters.CreateWildcardFilter(@"*/AppData/Local/Microsoft/Windows Store/*"),
DefaultFilters.CreateWildcardFilter(@"*/AppData/Local/Packages/*"), // https://superuser.com/questions/490925/explain-windows-8-windows-store-appdata-packages-and-what-to-backup
DefaultFilters.CreateWildcardFilter(@"*/AppData/Local/Temp*"),
DefaultFilters.CreateWildcardFilter(@"*/AppData/LocalLow/*"),
DefaultFilters.CreateWildcardFilter(@"*/AppData/Temp*"),
DefaultFilters.CreateWildcardFilter(@"*/Application Data/Apple Computer/Mobile Sync/*"),
DefaultFilters.CreateWildcardFilter(@"*/Application Data/Application Data*"),
DefaultFilters.CreateWildcardFilter(@"*/Cookies/*"),
DefaultFilters.CreateWildcardFilter(@"*/Google/Chrome/User Data/Default/Cookies"),
DefaultFilters.CreateWildcardFilter(@"*/Google/Chrome/User Data/Default/Cookies-journal"),
DefaultFilters.CreateWildcardFilter(@"*/I386*"),
DefaultFilters.CreateWildcardFilter(@"*/Internet Explorer/*"),
DefaultFilters.CreateWildcardFilter(@"*/Local Settings/History/*"),
DefaultFilters.CreateWildcardFilter(@"*/Local Settings/Temp*"),
DefaultFilters.CreateWildcardFilter(@"*/LocalService/*"),
DefaultFilters.CreateWildcardFilter(@"*/Microsoft*/RecoveryStore*"),
DefaultFilters.CreateWildcardFilter(@"*/Microsoft*/Windows/*.edb"),
DefaultFilters.CreateWildcardFilter(@"*/Microsoft*/Windows/*.log"),
DefaultFilters.CreateWildcardFilter(@"*/Microsoft*/Windows/Cookies*"),
DefaultFilters.CreateWildcardFilter(@"*/MSOCache*"),
DefaultFilters.CreateWildcardFilter(@"*/NetHood/*"),
DefaultFilters.CreateWildcardFilter(@"*/NetworkService/*"),
DefaultFilters.CreateWildcardFilter(@"*/NTUSER*"),
DefaultFilters.CreateWildcardFilter(@"*/ntuser.dat*"),
DefaultFilters.CreateWildcardFilter(@"*/PrintHood/*"),
DefaultFilters.CreateWildcardFilter(@"*/Recent/*"),
DefaultFilters.CreateWildcardFilter(@"*/RECYCLER/*"),
DefaultFilters.CreateWildcardFilter(@"*/Safari/Library/Caches/*"),
DefaultFilters.CreateWildcardFilter(@"*/SendTo/*"),
DefaultFilters.CreateWildcardFilter(@"*/Start Menu/*"),
DefaultFilters.CreateWildcardFilter(@"*/Temporary Internet Files/*"),
DefaultFilters.CreateWildcardFilter(@"*/Thumbs.db"),
DefaultFilters.CreateWildcardFilter(@"*UsrClass.dat"),
DefaultFilters.CreateWildcardFilter(@"*UsrClass.dat.LOG"),
DefaultFilters.CreateWildcardFilter(@"?:/autoexec.bat"),
DefaultFilters.CreateWildcardFilter(@"?:/Config.Msi*"),
DefaultFilters.CreateWildcardFilter(@"?:/hiberfil.sys"),
DefaultFilters.CreateWildcardFilter(@"?:/pagefile.sys"),
DefaultFilters.CreateWildcardFilter(@"?:/Program Files (x86)/*"),
DefaultFilters.CreateWildcardFilter(@"?:/Program Files/*"),
DefaultFilters.CreateWildcardFilter(@"?:/ProgramData/*"),
DefaultFilters.CreateWildcardFilter(@"?:/swapfile.sys"),
DefaultFilters.CreateWildcardFilter(@"?:/System Volume Information/*"),
DefaultFilters.CreateWildcardFilter(@"?:/Windows.old/*"),
DefaultFilters.CreateWildcardFilter(@"?:/Windows/*"),
DefaultFilters.CreateWildcardFilter(@"?:/Windows/Installer*"),
DefaultFilters.CreateWildcardFilter(@"?:/Windows/Temp*"),
};
var extra = GetWindowsRegistryFilters();
if (extra == null || extra.Length == 0)
return filters;
return filters.Union(extra).ToArray();
}
/// <summary>
/// Creates OSX filters
/// </summary>
/// <returns>OSX filters</returns>
private static string[] CreateOSXFilters()
{
return new[]
{
DefaultFilters.CreateWildcardFilter(@"*.fseventsd*"),
DefaultFilters.CreateWildcardFilter(@"*.hotfiles.btree*"),
DefaultFilters.CreateWildcardFilter(@"*.Spotlight-*/*"),
DefaultFilters.CreateWildcardFilter(@"*.Trash*"),
DefaultFilters.CreateWildcardFilter(@"*/Application Support/Google/Chrome/Default/Cookies"),
DefaultFilters.CreateWildcardFilter(@"*/Application Support/Google/Chrome/Default/Cookies-journal"),
DefaultFilters.CreateWildcardFilter(@"*/backups.backupdb/*"),
DefaultFilters.CreateWildcardFilter(@"*/iP* Software Updates/*"),
DefaultFilters.CreateWildcardFilter(@"*/iPhoto Library/iPod Photo Cache*"),
DefaultFilters.CreateWildcardFilter(@"*/iTunes/Album Artwork/Cache/*"),
DefaultFilters.CreateWildcardFilter(@"*/Library/Application Support/SyncServices/*"),
DefaultFilters.CreateWildcardFilter(@"*/Library/Caches/*"),
DefaultFilters.CreateWildcardFilter(@"*/Library/Calendars/*/Info.plist"),
DefaultFilters.CreateWildcardFilter(@"*/Library/Calendars/Calendar Cache"),
DefaultFilters.CreateWildcardFilter(@"*/Library/Cookies/com.apple.appstore.plist"),
DefaultFilters.CreateWildcardFilter(@"*/Library/Cookies/Cookies.binarycookies"),
DefaultFilters.CreateWildcardFilter(@"*/Library/Logs/*"),
DefaultFilters.CreateWildcardFilter(@"*/Library/Mail/*/Info.plist"),
DefaultFilters.CreateWildcardFilter(@"*/Library/Mail/AvailableFeeds/*"),
DefaultFilters.CreateWildcardFilter(@"*/Library/Mail/Envelope Index"),
DefaultFilters.CreateWildcardFilter(@"*/Library/Mirrors/*"),
DefaultFilters.CreateWildcardFilter(@"*/Library/PubSub/Database/*"),
DefaultFilters.CreateWildcardFilter(@"*/Library/PubSub/Downloads/*"),
DefaultFilters.CreateWildcardFilter(@"*/Library/PubSub/Feeds/*"),
DefaultFilters.CreateWildcardFilter(@"*/Library/Safari/HistoryIndex.sk"),
DefaultFilters.CreateWildcardFilter(@"*/Library/Safari/Icons.db"),
DefaultFilters.CreateWildcardFilter(@"*/Library/Safari/WebpageIcons.db"),
DefaultFilters.CreateWildcardFilter(@"*/Library/Saved Application State/*"),
DefaultFilters.CreateWildcardFilter(@"*/lost+found/*"),
DefaultFilters.CreateWildcardFilter(@"*/Microsoft User Data/Entourage Temp/*"),
DefaultFilters.CreateWildcardFilter(@"*/Network Trash Folder/*"),
DefaultFilters.CreateWildcardFilter(@"*/Trash/*"),
DefaultFilters.CreateWildcardFilter(@"*/VM Storage"),
DefaultFilters.CreateWildcardFilter(@"*Mobile.*Backups/*"),
DefaultFilters.CreateWildcardFilter(@"/.vol/*"),
DefaultFilters.CreateWildcardFilter(@"/afs/*"),
DefaultFilters.CreateWildcardFilter(@"/automount/*"),
DefaultFilters.CreateWildcardFilter(@"/bin/*"),
DefaultFilters.CreateWildcardFilter(@"/cores/*"),
DefaultFilters.CreateWildcardFilter(@"/Desktop DB"),
DefaultFilters.CreateWildcardFilter(@"/Desktop DF"),
DefaultFilters.CreateWildcardFilter(@"/dev/.*"),
DefaultFilters.CreateWildcardFilter(@"/etc/*"),
DefaultFilters.CreateWildcardFilter(@"/mach.sym"),
DefaultFilters.CreateWildcardFilter(@"/mach_kernel"),
DefaultFilters.CreateWildcardFilter(@"/net/*"),
DefaultFilters.CreateWildcardFilter(@"/Network/*"),
DefaultFilters.CreateWildcardFilter(@"/Network/Servers*"),
DefaultFilters.CreateWildcardFilter(@"/Previous Systems*"),
DefaultFilters.CreateWildcardFilter(@"/private/*"),
DefaultFilters.CreateWildcardFilter(@"/private/Network/*"),
DefaultFilters.CreateWildcardFilter(@"/private/tmp/*"),
DefaultFilters.CreateWildcardFilter(@"/private/var/automount/*"),
DefaultFilters.CreateWildcardFilter(@"/private/var/db/dhcpclient/*"),
DefaultFilters.CreateWildcardFilter(@"/private/var/db/fseventsd/*"),
DefaultFilters.CreateWildcardFilter(@"/private/var/folders/*"),
DefaultFilters.CreateWildcardFilter(@"/private/var/run/*"),
DefaultFilters.CreateWildcardFilter(@"/private/var/spool/postfix/*"),
DefaultFilters.CreateWildcardFilter(@"/private/var/tmp/*"),
DefaultFilters.CreateWildcardFilter(@"/private/var/vm/*"),
DefaultFilters.CreateWildcardFilter(@"/sbin/*"),
DefaultFilters.CreateWildcardFilter(@"/sw/*"),
DefaultFilters.CreateWildcardFilter(@"/System/*"),
DefaultFilters.CreateWildcardFilter(@"/System/Library/Extensions/Caches/*"),
DefaultFilters.CreateWildcardFilter(@"/tmp/*"),
DefaultFilters.CreateWildcardFilter(@"/Users/Shared/SC Info*"),
DefaultFilters.CreateWildcardFilter(@"/usr/*"),
};
}
/// <summary>
/// Creates Linux filters
/// </summary>
/// <returns>Linux filters</returns>
private static string[] CreateLinuxFilters()
{
return new[]
{
DefaultFilters.CreateWildcardFilter(@"*/.config/google-chrome/Default/Cookies"),
DefaultFilters.CreateWildcardFilter(@"*/.config/google-chrome/Default/Cookies-journal"),
DefaultFilters.CreateWildcardFilter(@"*/lost+found/*"),
DefaultFilters.CreateWildcardFilter(@"*~"),
DefaultFilters.CreateWildcardFilter(@"/bin/*"),
DefaultFilters.CreateWildcardFilter(@"/boot/*"),
DefaultFilters.CreateWildcardFilter(@"/dev/*"),
DefaultFilters.CreateWildcardFilter(@"/etc/*"),
DefaultFilters.CreateWildcardFilter(@"/initrd/*"),
DefaultFilters.CreateWildcardFilter(@"/lib/*"),
DefaultFilters.CreateWildcardFilter(@"/opt/*"),
DefaultFilters.CreateWildcardFilter(@"/proc/*"),
DefaultFilters.CreateWildcardFilter(@"/sbin/*"),
DefaultFilters.CreateWildcardFilter(@"/selinux/*"),
DefaultFilters.CreateWildcardFilter(@"/srv/*"),
DefaultFilters.CreateWildcardFilter(@"/sys/*"),
DefaultFilters.CreateWildcardFilter(@"/tmp/*"),
DefaultFilters.CreateWildcardFilter(@"/usr/*"),
DefaultFilters.CreateWildcardFilter(@"/var/*"),
};
}
/// <summary>
/// Creates a wildcard filter
/// </summary>
/// <param name="filter">Filter text</param>
/// <returns>Wildcard filter</returns>
private static string CreateWildcardFilter(string filter)
{
// Create a filter with the given name.
// However, in order to match paths correctly, the directory separators need to be normalized to match the system default.
return filter.Replace(System.IO.Path.AltDirectorySeparatorChar, System.IO.Path.DirectorySeparatorChar);
}
/// <summary>
/// Creates a Regex filter
/// </summary>
/// <param name="filter">Filter text</param>
/// <returns>Regex filter</returns>
private static string CreateRegexFilter(string filter)
{
// Create a filter with the given name.
// However, in order to match paths correctly, the directory separators need to be normalized to match the system default.
string escapedAlt = System.Text.RegularExpressions.Regex.Escape(System.IO.Path.AltDirectorySeparatorChar.ToString());
string escaped = System.Text.RegularExpressions.Regex.Escape(System.IO.Path.DirectorySeparatorChar.ToString());
return "[" + filter.Replace(escapedAlt, escaped) + "]";
}
/// <summary>
/// Wrapper method for getting a list of exclude paths from the Windows registry.
/// This method guards the call to <see cref="GetWindowsRegistryFiltersInternal"/> to avoid loader errors.
/// </summary>
/// <returns>The list of paths to exclude.</returns>
private static string[] GetWindowsRegistryFilters()
{
if (Utility.IsClientWindows)
{
// One Windows, filters may also be stored in the registry
try
{
return GetWindowsRegistryFiltersInternal();
}
catch
{
}
}
return null;
}
/// <summary>
/// Helper method that reads the Windows registry and finds paths to exclude.
/// This method should not be called directly as that could cause loader errors on Mono.
/// </summary>
/// <returns>The list of paths to exclude.</returns>
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
private static string[] GetWindowsRegistryFiltersInternal()
{
var rk = Microsoft.Win32.RegistryKey.OpenBaseKey(Microsoft.Win32.RegistryHive.LocalMachine, Microsoft.Win32.RegistryView.Default);
if (rk == null)
return new string[0];
var sk = rk.OpenSubKey(@"SYSTEM\CurrentControlSet\Control\BackupRestore\FilesNotToBackup");
if (sk == null)
return new string[0];
// Each value in this key is a potential path
return sk.GetValueNames()
.Where(x => x != null)
.SelectMany(x =>
{
var v = sk.GetValue(x);
@@ -421,15 +425,15 @@ namespace Duplicati.Library.Utility
return (string[])v;
else
return new string[0];
})
.Where(x => !string.IsNullOrWhiteSpace(x))
.Select(x => Environment.ExpandEnvironmentVariables(x))
.Where(x => !string.IsNullOrWhiteSpace(x))
.Where(x => x.IndexOfAny(System.IO.Path.GetInvalidPathChars()) < 0)
.Where(x => System.IO.Path.IsPathRooted(x))
.Select(x => x.EndsWith(" /s", StringComparison.OrdinalIgnoreCase) ? x.Substring(0, x.Length - 3).TrimEnd() : x)
.ToArray();
}
}
}
})
.Where(x => !string.IsNullOrWhiteSpace(x))
.Select(x => Environment.ExpandEnvironmentVariables(x))
.Where(x => !string.IsNullOrWhiteSpace(x))
.Where(x => x.IndexOfAny(System.IO.Path.GetInvalidPathChars()) < 0)
.Where(x => System.IO.Path.IsPathRooted(x))
.Select(x => x.EndsWith(" /s", StringComparison.OrdinalIgnoreCase) ? x.Substring(0, x.Length - 3).TrimEnd() : x)
.ToArray();
}
}
}
@@ -51,6 +51,7 @@
<Compile Include="OverrideableStream.cs" />
<Compile Include="ProgressReportingStream.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="ShaderStream.cs" />
<Compile Include="Sizeparser.cs" />
<Compile Include="SslCertificateValidator.cs" />
<Compile Include="TempFile.cs" />
@@ -102,7 +103,5 @@
<Name>Duplicati.Library.Localization</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Folder Include="Power\" />
</ItemGroup>
</Project>
<ItemGroup />
</Project>
+25 -8
View File
@@ -49,10 +49,22 @@ namespace Duplicati.Library.Utility
/// </summary>
public class FilterExpression : IFilter
{
/// <summary>
/// Implementation of a filter entry
/// </summary>
private struct FilterEntry
{
/// <summary>
/// The type of the filter
/// </summary>
public readonly FilterType Type;
/// <summary>
/// The filter string
/// </summary>
public readonly string Filter;
/// <summary>
/// The regular expression version of the filter
/// </summary>
public readonly System.Text.RegularExpressions.Regex Regexp;
/// <summary>
@@ -64,12 +76,18 @@ namespace Duplicati.Library.Utility
/// </summary>
private const char MULTIPLE_WILDCARD = '*';
/// <summary>
/// The regular expression flags
/// </summary>
private static readonly System.Text.RegularExpressions.RegexOptions REGEXP_OPTIONS =
System.Text.RegularExpressions.RegexOptions.Compiled |
System.Text.RegularExpressions.RegexOptions.ExplicitCapture |
(Library.Utility.Utility.IsFSCaseSensitive ? System.Text.RegularExpressions.RegexOptions.None : System.Text.RegularExpressions.RegexOptions.IgnoreCase);
/// <summary>
/// Initializes a new instance of the <see cref="T:Duplicati.Library.Utility.FilterExpression.FilterEntry"/> struct.
/// </summary>
/// <param name="filter">The filter string to use.</param>
public FilterEntry(string filter)
{
if (string.IsNullOrEmpty(filter))
@@ -78,7 +96,7 @@ namespace Duplicati.Library.Utility
this.Filter = null;
this.Regexp = null;
}
if (filter.StartsWith("[") && filter.EndsWith("]"))
else if (filter.StartsWith("[", StringComparison.Ordinal) && filter.EndsWith("]", StringComparison.Ordinal))
{
this.Type = FilterType.Regexp;
this.Filter = filter.Substring(1, filter.Length - 2);
@@ -255,7 +273,6 @@ namespace Duplicati.Library.Utility
/// <summary>
/// Creates a new <see cref="Duplicati.Library.Utility.FilterExpression"/> instance, representing an empty filter.
/// </summary>
/// <param name="filter">The filter string that represents the filter</param>
public FilterExpression()
: this((IEnumerable<string>)null, true)
{
@@ -302,7 +319,7 @@ namespace Duplicati.Library.Utility
if (string.IsNullOrWhiteSpace(filter))
return null;
if (filter.Length < 2 || (filter.StartsWith("[") && filter.EndsWith("]")))
if (filter.Length < 2 || (filter.StartsWith("[", StringComparison.Ordinal) && filter.EndsWith("]", StringComparison.Ordinal)))
return new string[] { filter };
else
return filter.Split(new char[] { System.IO.Path.PathSeparator }, StringSplitOptions.RemoveEmptyEntries);
@@ -340,7 +357,7 @@ namespace Duplicati.Library.Utility
first = false;
}
combined += "|(" + f.Regexp.ToString() + ")";
combined += "|(" + f.Regexp + ")";
}
}
@@ -584,9 +601,9 @@ namespace Duplicati.Library.Utility
foreach(var n in filters)
{
bool include;
if (n.StartsWith("+"))
if (n.StartsWith("+", StringComparison.Ordinal))
include = true;
else if (n.StartsWith("-"))
else if (n.StartsWith("-", StringComparison.Ordinal))
include = false;
else
continue;
@@ -65,7 +65,7 @@ namespace Duplicati.Library.Utility
else if (this.Second.Empty)
return this.First.ToString();
else
return "(" + this.First.ToString() + ") || (" + this.Second.ToString() + ")";
return "(" + this.First + ") || (" + this.Second + ")";
}
}
}
+75
View File
@@ -0,0 +1,75 @@
#region Disclaimer / License
// Copyright (C) 2015, The Duplicati Team
// http://www.duplicati.com, info@duplicati.com
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
//
#endregion
using System;
namespace Duplicati.Library.Utility
{
/// <summary>
/// A small utility stream that allows to keep streams open and counts the bytes sent through. </summary>
/// </summary>
public class ShaderStream : System.IO.Stream
{
private readonly System.IO.Stream m_baseStream;
private readonly bool m_keepBaseOpen;
private long m_read = 0;
private long m_written = 0;
public ShaderStream(System.IO.Stream baseStream, bool keepBaseOpen)
{
if (baseStream == null)
throw new ArgumentNullException(nameof(baseStream));
this.m_baseStream = baseStream;
this.m_keepBaseOpen = keepBaseOpen;
}
public long TotalBytesRead { get { return m_read; } }
public long TotalBytesWritten { get { return m_written; } }
public override bool CanRead { get { return m_baseStream.CanRead; } }
public override bool CanSeek { get { return m_baseStream.CanSeek; } }
public override bool CanWrite { get { return m_baseStream.CanWrite; } }
public override long Length { get { return m_baseStream.Length; } }
public override long Position
{
get { return m_baseStream.Position; }
set { m_baseStream.Position = value; }
}
public override void Flush() { m_baseStream.Flush(); }
public override long Seek(long offset, System.IO.SeekOrigin origin) { return m_baseStream.Seek(offset, origin); }
public override void SetLength(long value) { m_baseStream.SetLength(value); }
public override int Read(byte[] buffer, int offset, int count)
{
int r = m_baseStream.Read(buffer, offset, count);
m_read += r;
return r;
}
public override void Write(byte[] buffer, int offset, int count)
{
m_baseStream.Write(buffer, offset, count);
m_written += count;
}
protected override void Dispose(bool disposing)
{
if (disposing && !m_keepBaseOpen)
m_baseStream.Close();
base.Dispose(disposing);
}
}
}
+6 -6
View File
@@ -32,7 +32,7 @@ namespace Duplicati.Library.Utility
size = size.ToLower().Trim();
if (size.EndsWith("tb") || size.EndsWith("gb") || size.EndsWith("mb") || size.EndsWith("kb") || size.EndsWith("b"))
if (size.EndsWith("tb", StringComparison.Ordinal) || size.EndsWith("gb", StringComparison.Ordinal) || size.EndsWith("mb", StringComparison.Ordinal) || size.EndsWith("kb", StringComparison.Ordinal) || size.EndsWith("b", StringComparison.Ordinal))
return ParseSize(size);
else
return ParseSize(size + " " + defaultSuffix);
@@ -49,27 +49,27 @@ namespace Duplicati.Library.Utility
long factor = 1;
if (size.EndsWith("tb"))
if (size.EndsWith("tb", StringComparison.Ordinal))
{
factor = 1024L * 1024 * 1024 * 1024;
size = size.Substring(0, size.Length - 2).Trim();
}
else if (size.EndsWith("gb"))
else if (size.EndsWith("gb", StringComparison.Ordinal))
{
factor = 1024 * 1024 * 1024;
size = size.Substring(0, size.Length - 2).Trim();
}
else if (size.EndsWith("mb"))
else if (size.EndsWith("mb", StringComparison.Ordinal))
{
factor = 1024 * 1024;
size = size.Substring(0, size.Length - 2).Trim();
}
else if (size.EndsWith("kb"))
else if (size.EndsWith("kb", StringComparison.Ordinal))
{
factor = 1024;
size = size.Substring(0, size.Length - 2).Trim();
}
else if (size.EndsWith("b"))
else if (size.EndsWith("b", StringComparison.Ordinal))
size = size.Substring(0, size.Length - 1).Trim();
long r;
+5 -1
View File
@@ -72,13 +72,17 @@ namespace Duplicati.Library.Utility
m_fileTrace.Add(s, st);
return s;
}
#else
private static string GenerateUniqueName()
{
return APPLICATION_PREFIX + Guid.NewGuid().ToString();
}
#endif
/// <summary>
/// The name of the temp file
/// </summary>
public string Name => m_path;
/// <summary>
/// Gets all temporary files found in the current tempdir, that matches the application prefix
@@ -37,6 +37,11 @@ namespace Duplicati.Library.Utility
{
}
/// <summary>
/// Full name of the temp file.
/// </summary>
public string Name => m_file?.Name;
public TempFileStream(string file)
: this(TempFile.WrapExistingFile(file))
{
@@ -44,6 +49,8 @@ namespace Duplicati.Library.Utility
public TempFileStream(TempFile file)
{
if (file == null)
throw new ArgumentNullException(nameof(file));
m_file = file;
m_stream = System.IO.File.Open(file, FileMode.Create, FileAccess.ReadWrite, FileShare.None);
}
@@ -92,8 +92,6 @@ namespace Duplicati.Library.Utility
/// Creates a throttle around a stream.
/// </summary>
/// <param name="basestream">The stream to throttle</param>
/// <param name="readspeed">The maximum number of bytes pr. second to read. Specify a number less than 1 to allow unlimited speed.</param>
/// <param name="writespeed">The maximum number of bytes pr. second to write. Specify a number less than 1 to allow unlimited speed.</param>
public ThrottledStream(Stream basestream, Action<ThrottledStream> updateLimits)
: base(basestream)
{
@@ -217,11 +215,6 @@ namespace Duplicati.Library.Utility
/// <summary>
/// Calculates the speed, and inserts appropriate delays
/// </summary>
/// <param name="read">True if the operation is read, false otherwise</param>
/// <param name="buffer">The data buffer</param>
/// <param name="offset">The offset into the buffer</param>
/// <param name="count">The number of bytes to read or write</param>
/// <returns>The number of bytes processed while delaying</returns>
private void DelayIfRequired(ref long limit, int count, ref DateTime last_sample, ref long last_count, ref double current_speed)
{
var now = DateTime.Now;

Some files were not shown because too many files have changed in this diff Show More