Added ID's to each log message and each exception to allow later introduction of a Knowledgebase service that explains each error in more detail.
187 lines
7.3 KiB
C#
187 lines
7.3 KiB
C#
using Alphaleonis.Win32.Vss;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
|
|
namespace Duplicati.Library.Snapshots
|
|
{
|
|
public class MSSQLDB : IEquatable<MSSQLDB>
|
|
{
|
|
public string Name { get; }
|
|
public string ID { get; }
|
|
public List<string> DataPaths { get; }
|
|
|
|
public MSSQLDB(string Name, string ID, List<string> DataPaths)
|
|
{
|
|
this.Name = Name;
|
|
this.ID = ID;
|
|
this.DataPaths = DataPaths;
|
|
}
|
|
|
|
bool IEquatable<MSSQLDB>.Equals(MSSQLDB other)
|
|
{
|
|
return ID.Equals(other.ID);
|
|
}
|
|
|
|
public override int GetHashCode()
|
|
{
|
|
return ID.GetHashCode();
|
|
}
|
|
|
|
public override bool Equals(object obj)
|
|
{
|
|
MSSQLDB db = obj as MSSQLDB;
|
|
if (db != null)
|
|
{
|
|
return Equals(db);
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public static bool operator ==(MSSQLDB db1, MSSQLDB db2)
|
|
{
|
|
if (object.ReferenceEquals(db1, db2)) return true;
|
|
if (object.ReferenceEquals(db1, null)) return false;
|
|
if (object.ReferenceEquals(db2, null)) return false;
|
|
|
|
return db1.Equals(db2);
|
|
}
|
|
|
|
public static bool operator !=(MSSQLDB db1, MSSQLDB db2)
|
|
{
|
|
if (object.ReferenceEquals(db1, db2)) return false;
|
|
if (object.ReferenceEquals(db1, null)) return true;
|
|
if (object.ReferenceEquals(db2, null)) return true;
|
|
|
|
return !db1.Equals(db2);
|
|
}
|
|
}
|
|
|
|
public class MSSQLUtility
|
|
{
|
|
/// <summary>
|
|
/// The tag used for logging
|
|
/// </summary>
|
|
private static readonly string LOGTAG = Logging.Log.LogTagFromType<MSSQLUtility>();
|
|
/// <summary>
|
|
/// The MS SQL VSS Writer Guid
|
|
/// </summary>
|
|
public static readonly Guid MSSQLWriterGuid = new Guid("a65faa63-5ea8-4ebc-9dbd-a0c4db26912a");
|
|
/// <summary>
|
|
/// MS SQL is supported only on Windows platform
|
|
/// </summary>
|
|
public bool IsMSSQLInstalled { get; }
|
|
/// <summary>
|
|
/// Enumerated MS SQL DBs
|
|
/// </summary>
|
|
public List<MSSQLDB> DBs { get { return m_DBs; } }
|
|
private List<MSSQLDB> m_DBs;
|
|
|
|
public MSSQLUtility()
|
|
{
|
|
m_DBs = new List<MSSQLDB>();
|
|
|
|
if (!Utility.Utility.IsClientWindows)
|
|
{
|
|
IsMSSQLInstalled = false;
|
|
return;
|
|
}
|
|
|
|
string[] arrInstalledInstances = null;
|
|
|
|
var installed = Microsoft.Win32.Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server", "InstalledInstances", "");
|
|
if (installed is string)
|
|
{
|
|
if (!string.IsNullOrWhiteSpace(installed as string))
|
|
arrInstalledInstances = new string[] { installed as string };
|
|
}
|
|
else if (installed is string[])
|
|
arrInstalledInstances = (string[])installed;
|
|
else if (installed != null)
|
|
try { arrInstalledInstances = (string[])installed; }
|
|
catch { }
|
|
|
|
if(Environment.Is64BitOperatingSystem && arrInstalledInstances == null)
|
|
{
|
|
var installed32on64 = Microsoft.Win32.Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Microsoft SQL Server", "InstalledInstances", "");
|
|
if (installed32on64 is string)
|
|
{
|
|
if (!string.IsNullOrWhiteSpace(installed32on64 as string))
|
|
arrInstalledInstances = new string[] { installed32on64 as string };
|
|
}
|
|
else if (installed32on64 is string[])
|
|
arrInstalledInstances = (string[])installed32on64;
|
|
else if (installed32on64 != null)
|
|
try { arrInstalledInstances = (string[])installed32on64; }
|
|
catch { }
|
|
}
|
|
|
|
IsMSSQLInstalled = arrInstalledInstances == null ? false : arrInstalledInstances.Length > 0;
|
|
|
|
if (!IsMSSQLInstalled)
|
|
Logging.Log.WriteInformationMessage(LOGTAG, "NoMSSQLInstance", "Cannot find any MS SQL Server instance. MS SQL Server is probably not installed.");
|
|
}
|
|
|
|
/// <summary>
|
|
/// For all MS SQL databases it enumerate all associated paths using VSS data
|
|
/// </summary>
|
|
/// <returns>A collection of DBs and paths</returns>
|
|
public void QueryDBsInfo()
|
|
{
|
|
if (!IsMSSQLInstalled)
|
|
return;
|
|
|
|
m_DBs.Clear();
|
|
|
|
//Substitute for calling VssUtils.LoadImplementation(), as we have the dlls outside the GAC
|
|
string alphadir = Path.Combine(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), "alphavss");
|
|
string alphadll = Path.Combine(alphadir, VssUtils.GetPlatformSpecificAssemblyShortName() + ".dll");
|
|
IVssImplementation vss = (IVssImplementation)System.Reflection.Assembly.LoadFile(alphadll).CreateInstance("Alphaleonis.Win32.Vss.VssImplementation");
|
|
|
|
using (var m_backup = vss.CreateVssBackupComponents())
|
|
{
|
|
m_backup.InitializeForBackup(null);
|
|
m_backup.SetContext(VssSnapshotContext.Backup);
|
|
m_backup.SetBackupState(false, true, VssBackupType.Full, false);
|
|
m_backup.EnableWriterClasses(new Guid[] { MSSQLWriterGuid });
|
|
|
|
try
|
|
{
|
|
m_backup.GatherWriterMetadata();
|
|
var writerMetaData = m_backup.WriterMetadata.FirstOrDefault(o => o.WriterId.Equals(MSSQLWriterGuid));
|
|
|
|
if (writerMetaData == null)
|
|
throw new Duplicati.Library.Interface.UserInformationException("Microsoft SQL Server VSS Writer not found - cannot backup SQL databases.", "NoMsSqlVssWriter");
|
|
|
|
foreach (var component in writerMetaData.Components)
|
|
{
|
|
var paths = new List<string>();
|
|
|
|
foreach (var file in component.Files)
|
|
if (file.FileSpecification.Contains("*"))
|
|
{
|
|
if (Directory.Exists(Utility.Utility.AppendDirSeparator(file.Path)))
|
|
paths.Add(Utility.Utility.AppendDirSeparator(file.Path));
|
|
}
|
|
else
|
|
{
|
|
if (File.Exists(Path.Combine(file.Path, file.FileSpecification)))
|
|
paths.Add(Path.Combine(file.Path, file.FileSpecification));
|
|
}
|
|
|
|
m_DBs.Add(new MSSQLDB(component.ComponentName, component.LogicalPath + "\\" + component.ComponentName, paths.Distinct(Utility.Utility.ClientFilenameStringComparer).OrderBy(a => a).ToList()));
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
m_backup.FreeWriterMetadata();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|