Merge branch 'fix/sqlite_file_permissions' of git://github.com/verhoek/duplicati into verhoek-fix/sqlite_file_permissions
This commit is contained in:
@@ -182,7 +182,7 @@ namespace Duplicati.Library.SQLiteHelper
|
||||
/// </summary>
|
||||
/// <param name="connection">The database connection to use</param>
|
||||
/// <param name="sourcefile">The file the database is placed in</param>
|
||||
public static void UpgradeDatabase(IDbConnection connection, string sourcefile, string schema, IList<string> versions)
|
||||
private static void UpgradeDatabase(IDbConnection connection, string sourcefile, string schema, IList<string> versions)
|
||||
{
|
||||
if (connection.State != ConnectionState.Open)
|
||||
{
|
||||
|
||||
@@ -32,10 +32,16 @@
|
||||
<ConsolePause>false</ConsolePause>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(OS)' != 'Windows_NT' ">
|
||||
<DefineConstants>_NOT_WINDOWS</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Data" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition=" '$(OS)' != 'Windows_NT' ">
|
||||
<Reference Include="Mono.Posix" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="DBSchemaUpgrades\DbUpgradesRegistry.cs" />
|
||||
<Compile Include="DBSchemaUpgrades\IDbSchemaUpgrade.cs" />
|
||||
@@ -86,4 +92,4 @@
|
||||
<Name>Duplicati.Library.Logging</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
@@ -19,22 +19,87 @@
|
||||
#endregion
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace Duplicati.Library.SQLiteHelper
|
||||
{
|
||||
public static class SQLiteLoader
|
||||
{
|
||||
/// <summary>
|
||||
{
|
||||
/// <summary>
|
||||
/// The tag used for logging
|
||||
/// </summary>
|
||||
private static readonly string LOGTAG = Logging.Log.LogTagFromType(typeof(SQLiteLoader));
|
||||
private static readonly string LOGTAG = Logging.Log.LogTagFromType(typeof(SQLiteLoader));
|
||||
|
||||
/// <summary>
|
||||
/// A cached copy of the type
|
||||
/// </summary>
|
||||
private static Type m_type = null;
|
||||
|
||||
|
||||
private const string SQLiteAssembly = "System.Data.SQLite.dll";
|
||||
|
||||
/// <summary>
|
||||
/// Helper method with logic to handle opening a database in possibly encrypted format
|
||||
/// </summary>
|
||||
/// <param name="con">The SQLite connection object</param>
|
||||
/// <param name="DatabasePath">The location of Duplicati's database.</param>
|
||||
/// <param name="useDatabaseEncryption">Specify if database is encrypted</param>
|
||||
/// <param name="password">Encryption password</param>
|
||||
public static void OpenDatabase(System.Data.IDbConnection con, string DatabasePath, bool useDatabaseEncryption, string password)
|
||||
{
|
||||
System.Reflection.MethodInfo setPwdMethod = con.GetType().GetMethod("SetPassword", new[] { typeof(string) });
|
||||
string attemptedPassword;
|
||||
|
||||
if (!useDatabaseEncryption || string.IsNullOrEmpty(password))
|
||||
attemptedPassword = null; //No encryption specified, attempt to open without
|
||||
else
|
||||
attemptedPassword = password; //Encryption specified, attempt to open with
|
||||
|
||||
if (setPwdMethod != null)
|
||||
setPwdMethod.Invoke(con, new object[] { attemptedPassword });
|
||||
|
||||
try
|
||||
{
|
||||
//Attempt to open in preferred state
|
||||
OpenSQLiteFile(con, DatabasePath);
|
||||
|
||||
TestSQLiteFile(con);
|
||||
}
|
||||
catch
|
||||
{
|
||||
try
|
||||
{
|
||||
//We can't try anything else without a password
|
||||
if (string.IsNullOrEmpty(password))
|
||||
throw;
|
||||
|
||||
//Open failed, now try the reverse
|
||||
attemptedPassword = attemptedPassword == null ? password : null;
|
||||
|
||||
con.Close();
|
||||
if (setPwdMethod != null)
|
||||
setPwdMethod.Invoke(con, new object[] { attemptedPassword });
|
||||
OpenSQLiteFile(con, DatabasePath);
|
||||
|
||||
TestSQLiteFile(con);
|
||||
}
|
||||
catch
|
||||
{
|
||||
try { con.Close(); }
|
||||
catch { }
|
||||
}
|
||||
|
||||
//If the db is not open now, it won't open
|
||||
if (con.State != System.Data.ConnectionState.Open)
|
||||
throw; //Report original error
|
||||
|
||||
//The open method succeeded with the non-default method, now change the password
|
||||
System.Reflection.MethodInfo changePwdMethod = con.GetType().GetMethod("ChangePassword", new[] { typeof(string) });
|
||||
changePwdMethod.Invoke(con, new object[] { useDatabaseEncryption ? password : null });
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads an SQLite connection instance, optionally setting the tempfolder and opening the the database
|
||||
/// </summary>
|
||||
@@ -56,8 +121,7 @@ namespace Duplicati.Library.SQLiteHelper
|
||||
con = (System.Data.IDbConnection)Activator.CreateInstance(Duplicati.Library.SQLiteHelper.SQLiteLoader.SQLiteConnectionType);
|
||||
if (!string.IsNullOrWhiteSpace(targetpath))
|
||||
{
|
||||
con.ConnectionString = "Data Source=" + targetpath;
|
||||
con.Open();
|
||||
OpenSQLiteFile(con, targetpath);
|
||||
|
||||
// Try to set the temp_dir even tough it is deprecated
|
||||
if (!string.IsNullOrWhiteSpace(tempdir))
|
||||
@@ -88,9 +152,9 @@ namespace Duplicati.Library.SQLiteHelper
|
||||
finally
|
||||
{
|
||||
System.Environment.SetEnvironmentVariable("SQLITE_TMPDIR", prev);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
return con;
|
||||
}
|
||||
|
||||
@@ -103,7 +167,6 @@ namespace Duplicati.Library.SQLiteHelper
|
||||
{
|
||||
if (m_type == null)
|
||||
{
|
||||
var filename = "System.Data.SQLite.dll";
|
||||
var basePath = System.IO.Path.Combine(System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), "SQLite");
|
||||
|
||||
// Set this to make SQLite preload automatically
|
||||
@@ -117,12 +180,12 @@ namespace Duplicati.Library.SQLiteHelper
|
||||
//If we run with MS.Net we can use the mixed mode assemblies
|
||||
if (Environment.Is64BitProcess)
|
||||
{
|
||||
if (System.IO.File.Exists(System.IO.Path.Combine(System.IO.Path.Combine(basePath, "win64"), filename)))
|
||||
if (System.IO.File.Exists(System.IO.Path.Combine(System.IO.Path.Combine(basePath, "win64"), SQLiteAssembly)))
|
||||
assemblyPath = System.IO.Path.Combine(basePath, "win64");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (System.IO.File.Exists(System.IO.Path.Combine(System.IO.Path.Combine(basePath, "win32"), filename)))
|
||||
if (System.IO.File.Exists(System.IO.Path.Combine(System.IO.Path.Combine(basePath, "win32"), SQLiteAssembly)))
|
||||
assemblyPath = System.IO.Path.Combine(basePath, "win32");
|
||||
}
|
||||
|
||||
@@ -134,16 +197,18 @@ namespace Duplicati.Library.SQLiteHelper
|
||||
catch { }
|
||||
}
|
||||
|
||||
} else {
|
||||
//On Mono, we try to find the Mono version of SQLite
|
||||
|
||||
//This secret environment variable can be used to support older installations
|
||||
}
|
||||
else
|
||||
{
|
||||
//On Mono, we try to find the Mono version of SQLite
|
||||
|
||||
//This secret environment variable can be used to support older installations
|
||||
var envvalue = System.Environment.GetEnvironmentVariable("DISABLE_MONO_DATA_SQLITE");
|
||||
if (!Utility.Utility.ParseBool(envvalue, envvalue != null))
|
||||
{
|
||||
foreach(var asmversion in new string[] {"4.0.0.0", "2.0.0.0"})
|
||||
foreach (var asmversion in new string[] { "4.0.0.0", "2.0.0.0" })
|
||||
{
|
||||
try
|
||||
try
|
||||
{
|
||||
Type t = System.Reflection.Assembly.Load(string.Format("Mono.Data.Sqlite, Version={0}, Culture=neutral, PublicKeyToken=0738eb9f132ed756", asmversion)).GetType("Mono.Data.Sqlite.SqliteConnection");
|
||||
if (t != null && t.GetInterface("System.Data.IDbConnection", false) != null)
|
||||
@@ -154,22 +219,57 @@ namespace Duplicati.Library.SQLiteHelper
|
||||
m_type = t;
|
||||
return m_type;
|
||||
}
|
||||
}
|
||||
|
||||
} catch {
|
||||
}
|
||||
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
Logging.Log.WriteVerboseMessage(LOGTAG, "FailedToLoadSQLite", "Failed to load Mono.Data.Sqlite.SqliteConnection, reverting to built-in.");
|
||||
}
|
||||
|
||||
Logging.Log.WriteVerboseMessage(LOGTAG, "FailedToLoadSQLite", "Failed to load Mono.Data.Sqlite.SqliteConnection, reverting to built-in.");
|
||||
}
|
||||
}
|
||||
|
||||
m_type = System.Reflection.Assembly.LoadFile(System.IO.Path.Combine(assemblyPath, filename)).GetType("System.Data.SQLite.SQLiteConnection");
|
||||
m_type = System.Reflection.Assembly.LoadFile(System.IO.Path.Combine(assemblyPath, SQLiteAssembly)).GetType("System.Data.SQLite.SQLiteConnection");
|
||||
}
|
||||
|
||||
return m_type;
|
||||
}
|
||||
}
|
||||
|
||||
private static void OpenSQLiteFile(System.Data.IDbConnection con, string path)
|
||||
{
|
||||
// Check if SQLite database exists before opening a connection to it.
|
||||
// This information is used to 'fix' permissions on a newly created file.
|
||||
#if _NOT_WINDOWS
|
||||
bool fileExists = false;
|
||||
if (!Library.Utility.Utility.IsClientWindows)
|
||||
{
|
||||
fileExists = File.Exists(path);
|
||||
}
|
||||
#endif
|
||||
|
||||
con.ConnectionString = "Data Source=" + path;
|
||||
con.Open();
|
||||
|
||||
#if _NOT_WINDOWS
|
||||
if (!Library.Utility.Utility.IsClientWindows && !fileExists)
|
||||
{
|
||||
Mono.Unix.Native.Syscall.chmod(path, Mono.Unix.Native.FilePermissions.S_IRUSR | Mono.Unix.Native.FilePermissions.S_IWUSR);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
private static void TestSQLiteFile(System.Data.IDbConnection con)
|
||||
{
|
||||
// Do a dummy query to make sure we have a working db
|
||||
using (var cmd = con.CreateCommand())
|
||||
{
|
||||
cmd.CommandText = "SELECT COUNT(*) FROM SQLITE_MASTER";
|
||||
cmd.ExecuteScalar();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -596,10 +596,9 @@ namespace Duplicati.Server
|
||||
#else
|
||||
var useDatabaseEncryption = !commandlineOptions.ContainsKey("unencrypted-database") || !Library.Utility.Utility.ParseBool(commandlineOptions["unencrypted-database"], true);
|
||||
#endif
|
||||
con.ConnectionString = "Data Source=" + DatabasePath;
|
||||
|
||||
//Attempt to open the database, handling any encryption present
|
||||
OpenDatabase(con, useDatabaseEncryption, dbPassword);
|
||||
Duplicati.Library.SQLiteHelper.SQLiteLoader.OpenDatabase(con, DatabasePath, useDatabaseEncryption, dbPassword);
|
||||
|
||||
Duplicati.Library.SQLiteHelper.DatabaseUpgrader.UpgradeDatabase(con, DatabasePath, typeof(Database.Connection));
|
||||
}
|
||||
@@ -692,77 +691,7 @@ namespace Duplicati.Server
|
||||
|
||||
StatusEventNotifyer.SignalNewEvent();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper method with logic to handle opening a database in possibly encrypted format
|
||||
/// </summary>
|
||||
/// <param name="con">The SQLite connection object</param>
|
||||
/// <param name="useDatabaseEncryption">Specify if database is encrypted</param>
|
||||
/// <param name="password">Encryption password</param>
|
||||
public static void OpenDatabase(System.Data.IDbConnection con, bool useDatabaseEncryption, string password)
|
||||
{
|
||||
System.Reflection.MethodInfo setPwdMethod = con.GetType().GetMethod("SetPassword", new Type[] { typeof(string) });
|
||||
string attemptedPassword;
|
||||
|
||||
if (!useDatabaseEncryption || string.IsNullOrEmpty(password))
|
||||
attemptedPassword = null; //No encryption specified, attempt to open without
|
||||
else
|
||||
attemptedPassword = password; //Encryption specified, attempt to open with
|
||||
|
||||
if (setPwdMethod != null)
|
||||
setPwdMethod.Invoke(con, new object[] { attemptedPassword });
|
||||
|
||||
try
|
||||
{
|
||||
//Attempt to open in preferred state
|
||||
con.Open();
|
||||
|
||||
// Do a dummy query to make sure we have a working db
|
||||
using (var cmd = con.CreateCommand())
|
||||
{
|
||||
cmd.CommandText = "SELECT COUNT(*) FROM SQLITE_MASTER";
|
||||
cmd.ExecuteScalar();
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
try
|
||||
{
|
||||
//We can't try anything else without a password
|
||||
if (string.IsNullOrEmpty(password))
|
||||
throw;
|
||||
|
||||
//Open failed, now try the reverse
|
||||
attemptedPassword = attemptedPassword == null ? password : null;
|
||||
|
||||
con.Close();
|
||||
if (setPwdMethod != null)
|
||||
setPwdMethod.Invoke(con, new object[] { attemptedPassword });
|
||||
con.Open();
|
||||
|
||||
// Do a dummy query to make sure we have a working db
|
||||
using (var cmd = con.CreateCommand())
|
||||
{
|
||||
cmd.CommandText = "SELECT COUNT(*) FROM SQLITE_MASTER";
|
||||
cmd.ExecuteScalar();
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
try { con.Close(); }
|
||||
catch { }
|
||||
}
|
||||
|
||||
//If the db is not open now, it won't open
|
||||
if (con.State != System.Data.ConnectionState.Open)
|
||||
throw; //Report original error
|
||||
|
||||
//The open method succeeded with the non-default method, now change the password
|
||||
System.Reflection.MethodInfo changePwdMethod = con.GetType().GetMethod("ChangePassword", new Type[] { typeof(string) });
|
||||
changePwdMethod.Invoke(con, new object[] { useDatabaseEncryption ? password : null });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Simple method for tracking if the server has crashed
|
||||
/// </summary>
|
||||
|
||||
Reference in New Issue
Block a user