Added support for symbolic links on non-Windows platforms.

This commit is contained in:
2017-08-22 18:05:18 +01:00
parent a874eb48a3
commit a2b6c4a828
7 changed files with 261 additions and 51 deletions

View File

@@ -54,6 +54,7 @@ namespace osrepodbmgr.Core
public static bool usableDotNetZip;
public static string clamdVersion;
public static bool virusTotalEnabled;
public static Dictionary<string, string> symlinks;
public delegate void UnarChangeStatusDelegate();
public static event UnarChangeStatusDelegate UnarChangeStatus;

View File

@@ -659,6 +659,18 @@ namespace osrepodbmgr.Core
trans = dbCon.BeginTransaction();
dbcmd.Transaction = trans;
sql = string.Format("DROP TABLE IF EXISTS `os_{0}_symlinks`;", id);
dbcmd.CommandText = sql;
dbcmd.ExecuteNonQuery();
trans.Commit();
dbcmd.Dispose();
dbcmd = dbCon.CreateCommand();
trans = dbCon.BeginTransaction();
dbcmd.Transaction = trans;
sql = string.Format("DELETE FROM oses WHERE id = '{0}';", id);
dbcmd.CommandText = sql;
@@ -866,6 +878,95 @@ namespace osrepodbmgr.Core
return true;
}
public bool HasSymlinks(long osId)
{
IDbCommand dbcmd = dbCon.CreateCommand();
dbcmd.CommandText = string.Format("SELECT COUNT(*) FROM sqlite_master WHERE type = 'table' AND name = 'os_{0}_symlinks'", osId);
object count = dbcmd.ExecuteScalar();
dbcmd.Dispose();
return Convert.ToUInt64(count) > 0;
}
public bool CreateSymlinkTableForOS(long id)
{
IDbCommand dbcmd = dbCon.CreateCommand();
IDbTransaction trans = dbCon.BeginTransaction();
dbcmd.Transaction = trans;
string sql = string.Format("DROP TABLE IF EXISTS `os_{0}_symlinks`;\n\n" +
"CREATE TABLE IF NOT EXISTS `os_{0}_symlinks` (\n" +
" `path` VARCHAR(8192) PRIMARY KEY,\n" +
" `target` VARCHAR(8192) NOT NULL);\n\n" +
"CREATE UNIQUE INDEX `os_{0}_symlinks_path_UNIQUE` ON `os_{0}_symlinks` (`path` ASC);\n\n" +
"CREATE INDEX `os_{0}_symlinks_target_idx` ON `os_{0}_symlinks` (`target` ASC);", id);
dbcmd.CommandText = sql;
dbcmd.ExecuteNonQuery();
trans.Commit();
dbcmd.Dispose();
return true;
}
public bool AddSymlinkToOS(string path, string target, long os)
{
IDbCommand dbcmd = dbCon.CreateCommand();
IDbDataParameter param1 = dbcmd.CreateParameter();
IDbDataParameter param2 = dbcmd.CreateParameter();
param1.ParameterName = "@path";
param2.ParameterName = "@target";
param1.DbType = DbType.String;
param2.DbType = DbType.String;
param1.Value = path;
param2.Value = target;
dbcmd.Parameters.Add(param1);
dbcmd.Parameters.Add(param2);
IDbTransaction trans = dbCon.BeginTransaction();
dbcmd.Transaction = trans;
string sql = string.Format("INSERT INTO `os_{0}_symlinks` (`path`, `target`)" +
" VALUES (@path, @target)", os);
dbcmd.CommandText = sql;
dbcmd.ExecuteNonQuery();
trans.Commit();
dbcmd.Dispose();
return true;
}
public bool GetAllSymlinks(out Dictionary<string, string> entries, long id)
{
entries = new Dictionary<string, string>();
string sql = string.Format("SELECT * from os_{0}_symlinks", id);
IDbCommand dbcmd = dbCon.CreateCommand();
IDbDataAdapter dataAdapter = dbCore.GetNewDataAdapter();
dbcmd.CommandText = sql;
DataSet dataSet = new DataSet();
dataAdapter.SelectCommand = dbcmd;
dataAdapter.Fill(dataSet);
DataTable dataTable = dataSet.Tables[0];
foreach(DataRow dRow in dataTable.Rows)
{
if(!entries.ContainsKey(dRow["path"].ToString()))
entries.Add(dRow["path"].ToString(), dRow["target"].ToString());
}
return true;
}
}
}

View File

@@ -0,0 +1,35 @@
using System;
using System.Runtime.InteropServices;
using System.Text;
namespace osrepodbmgr.Core
{
public static class Symlinks
{
[DllImport("libc", CharSet = CharSet.Ansi, SetLastError = true)]
internal static extern int readlink(string path, IntPtr buf, int bufsize);
public static string ReadLink(string path)
{
IntPtr buf = Marshal.AllocHGlobal(16384);
int ret = readlink(path, buf, 16384);
if(ret < 0)
return null;
byte[] target = new byte[ret];
Marshal.Copy(buf, target, 0, ret);
return Encoding.UTF8.GetString(target);
}
[DllImport("libc", CharSet = CharSet.Ansi, SetLastError = true)]
internal static extern int symlink(string target, string path);
public static int Symlink(string target, string path)
{
return symlink(target, path);
}
}
}

View File

@@ -366,29 +366,11 @@ namespace osrepodbmgr.Core
#endif
long counter = 0;
string format = null;
bool isLink = false;
JsonTextReader jsReader = new JsonTextReader(new StringReader(lsarOutput));
while(jsReader.Read())
{
if(jsReader.TokenType == JsonToken.PropertyName && jsReader.Value != null && jsReader.Value.ToString() == "XADFileName")
counter++;
else if(jsReader.TokenType == JsonToken.PropertyName && jsReader.Value != null && jsReader.Value.ToString() == "XADIsLink")
{
jsReader.Read();
if(jsReader.TokenType == JsonToken.Integer)
isLink = int.Parse(jsReader.Value.ToString()) > 0;
}
else if(jsReader.TokenType == JsonToken.PropertyName && jsReader.Value != null && jsReader.Value.ToString() == "XADIsHardLink")
{
jsReader.Read();
// TODO: Support symlinks, devices, hardlinks, whatever?
if(jsReader.TokenType == JsonToken.Integer && isLink && int.Parse(jsReader.Value.ToString()) == 0)
{
if(Failed != null)
Failed("Archive contains unsupported symbolic links, not continuing.");
return;
}
}
else if(jsReader.TokenType == JsonToken.PropertyName && jsReader.Value != null && jsReader.Value.ToString() == "lsarFormatName")
{
jsReader.Read();
@@ -637,6 +619,13 @@ namespace osrepodbmgr.Core
return;
}
if(dbCore.DBOps.HasSymlinks(Context.dbInfo.id))
{
if(Failed != null)
Failed("Cannot create symbolic links on ZIP files");
return;
}
if(!Context.usableDotNetZip)
{
if(Failed != null)

View File

@@ -293,6 +293,24 @@ namespace osrepodbmgr.Core
#if DEBUG
stopwatch.Stop();
Console.WriteLine("Core.AddFilesToDb(): Took {0} seconds to add all folders to the database", stopwatch.Elapsed.TotalSeconds);
stopwatch.Restart();
#endif
counter = 0;
if(Context.symlinks.Count > 0)
dbCore.DBOps.CreateSymlinkTableForOS(Context.dbInfo.id);
foreach(KeyValuePair<string, string> kvp in Context.symlinks)
{
if(UpdateProgress != null)
UpdateProgress(null, "Adding symbolic links to OS in database", counter, Context.symlinks.Count);
dbCore.DBOps.AddSymlinkToOS(kvp.Key, kvp.Value, Context.dbInfo.id);
counter++;
}
#if DEBUG
stopwatch.Stop();
Console.WriteLine("Core.AddFilesToDb(): Took {0} seconds to add all symbolic links to the database", stopwatch.Elapsed.TotalSeconds);
#endif
if(Finished != null)

View File

@@ -39,6 +39,7 @@ using Schemas;
using SharpCompress.Compressors.BZip2;
using SharpCompress.Compressors.Deflate;
using SharpCompress.Compressors.LZMA;
using DiscImageChef.Interop;
namespace osrepodbmgr.Core
{
@@ -106,8 +107,14 @@ namespace osrepodbmgr.Core
{
Context.hashes = new Dictionary<string, DBOSFile>();
Context.foldersDict = new Dictionary<string, DBFolder>();
Context.symlinks = new Dictionary<string, string>();
List<string> alreadyMetadata = new List<string>();
bool foundMetadata = false;
bool symlinksSupported = DetectOS.GetRealPlatformID() != DiscImageChef.Interop.PlatformID.WinCE &&
DetectOS.GetRealPlatformID() != DiscImageChef.Interop.PlatformID.Win32S &&
DetectOS.GetRealPlatformID() != DiscImageChef.Interop.PlatformID.Win32NT &&
DetectOS.GetRealPlatformID() != DiscImageChef.Interop.PlatformID.Win32Windows &&
DetectOS.GetRealPlatformID() != DiscImageChef.Interop.PlatformID.WindowsPhone;
// For metadata
List<ArchitecturesTypeArchitecture> architectures = new List<ArchitecturesTypeArchitecture>();
@@ -159,52 +166,52 @@ namespace osrepodbmgr.Core
XmlSerializer xs = new XmlSerializer(typeof(CICMMetadataType));
try
{
if (xs.CanDeserialize(xr))
if(xs.CanDeserialize(xr))
{
CICMMetadataType thisMetadata = (CICMMetadataType)xs.Deserialize(xr);
if (thisMetadata.Architectures != null)
if(thisMetadata.Architectures != null)
architectures.AddRange(thisMetadata.Architectures);
if (thisMetadata.Barcodes != null)
if(thisMetadata.Barcodes != null)
barcodes.AddRange(thisMetadata.Barcodes);
if (thisMetadata.BlockMedia != null)
if(thisMetadata.BlockMedia != null)
disks.AddRange(thisMetadata.BlockMedia);
if (thisMetadata.Categories != null)
if(thisMetadata.Categories != null)
categories.AddRange(thisMetadata.Categories);
if (thisMetadata.Keywords != null)
if(thisMetadata.Keywords != null)
keywords.AddRange(thisMetadata.Keywords);
if (thisMetadata.Languages != null)
if(thisMetadata.Languages != null)
languages.AddRange(thisMetadata.Languages);
if (thisMetadata.OpticalDisc != null)
if(thisMetadata.OpticalDisc != null)
discs.AddRange(thisMetadata.OpticalDisc);
if (thisMetadata.Subcategories != null)
if(thisMetadata.Subcategories != null)
subcategories.AddRange(thisMetadata.Subcategories);
if (thisMetadata.Systems != null)
if(thisMetadata.Systems != null)
systems.AddRange(thisMetadata.Systems);
if (thisMetadata.Author != null)
if(thisMetadata.Author != null)
authors.AddRange(thisMetadata.Author);
if (thisMetadata.Developer != null)
if(thisMetadata.Developer != null)
developers.AddRange(thisMetadata.Developer);
if (thisMetadata.Performer != null)
if(thisMetadata.Performer != null)
performers.AddRange(thisMetadata.Performer);
if (thisMetadata.Publisher != null)
if(thisMetadata.Publisher != null)
publishers.AddRange(thisMetadata.Publisher);
if (string.IsNullOrWhiteSpace(metadataName) && !string.IsNullOrWhiteSpace(thisMetadata.Name))
if(string.IsNullOrWhiteSpace(metadataName) && !string.IsNullOrWhiteSpace(thisMetadata.Name))
metadataName = thisMetadata.Name;
if (string.IsNullOrWhiteSpace(metadataPartNo) && !string.IsNullOrWhiteSpace(thisMetadata.PartNumber))
if(string.IsNullOrWhiteSpace(metadataPartNo) && !string.IsNullOrWhiteSpace(thisMetadata.PartNumber))
metadataPartNo = thisMetadata.PartNumber;
if (string.IsNullOrWhiteSpace(metadataSerial) && !string.IsNullOrWhiteSpace(thisMetadata.SerialNumber))
if(string.IsNullOrWhiteSpace(metadataSerial) && !string.IsNullOrWhiteSpace(thisMetadata.SerialNumber))
metadataSerial = thisMetadata.SerialNumber;
if (string.IsNullOrWhiteSpace(metadataVersion) && !string.IsNullOrWhiteSpace(thisMetadata.Version))
if(string.IsNullOrWhiteSpace(metadataVersion) && !string.IsNullOrWhiteSpace(thisMetadata.Version))
metadataVersion = thisMetadata.Version;
if (thisMetadata.ReleaseDateSpecified)
if(thisMetadata.ReleaseDateSpecified)
{
if (thisMetadata.ReleaseDate > releaseDate)
if(thisMetadata.ReleaseDate > releaseDate)
{
releaseDateSpecified = true;
releaseDate = thisMetadata.ReleaseDate;
}
}
if (thisMetadata.ReleaseTypeSpecified)
if(thisMetadata.ReleaseTypeSpecified)
{
releaseTypeSpecified = true;
releaseType = thisMetadata.ReleaseType;
@@ -243,7 +250,7 @@ namespace osrepodbmgr.Core
continue;
}
}
catch (XmlException)
catch(XmlException)
{
xr.Close();
xrs.Close();
@@ -355,12 +362,30 @@ namespace osrepodbmgr.Core
string relpath = file.Substring(filesPath.Length + 1);
// TODO: Support symlinks, devices, hardlinks, whatever?
if(fi.Attributes.HasFlag(FileAttributes.ReparsePoint))
{
if(Failed != null)
Failed(string.Format("{0} is an unsupported symbolic link, not continuing.", relpath));
return;
// TODO: Symlinks not supported on any Windows platform
if(!symlinksSupported)
{
if(Failed != null)
Failed(string.Format("{0} is an unsupported symbolic link, not continuing.", relpath));
return;
}
if(UpdateProgress != null)
UpdateProgress(string.Format("Resolving symlink on file {0} of {1}", counter, Context.files.Count), null, counter, Context.files.Count);
string target = Symlinks.ReadLink(file);
if(target == null)
{
if(Failed != null)
Failed(string.Format("Could not resolve symbolic link at {0}, not continuing.", relpath));
return;
}
Context.symlinks.Add(relpath, target);
counter++;
continue;
}
if(UpdateProgress != null)
@@ -650,8 +675,15 @@ namespace osrepodbmgr.Core
return;
}
bool symlinksSupported = DetectOS.GetRealPlatformID() != DiscImageChef.Interop.PlatformID.WinCE &&
DetectOS.GetRealPlatformID() != DiscImageChef.Interop.PlatformID.Win32S &&
DetectOS.GetRealPlatformID() != DiscImageChef.Interop.PlatformID.Win32NT &&
DetectOS.GetRealPlatformID() != DiscImageChef.Interop.PlatformID.Win32Windows &&
DetectOS.GetRealPlatformID() != DiscImageChef.Interop.PlatformID.WindowsPhone;
List<DBOSFile> files;
List<DBFolder> folders;
Dictionary<string, string> symlinks = new Dictionary<string, string>();
long counter;
if(UpdateProgress != null)
@@ -665,7 +697,22 @@ namespace osrepodbmgr.Core
dbCore.DBOps.GetAllFolders(out folders, Context.dbInfo.id);
if(UpdateProgress != null)
UpdateProgress("", "Creating folders...", 3, 100);
UpdateProgress("", "Asking DB for symbolic links...", 3, 100);
if(dbCore.DBOps.HasSymlinks(Context.dbInfo.id))
{
if(!symlinksSupported)
{
if(Failed != null)
Failed("Symbolic links cannot be created on this platform.");
return;
}
dbCore.DBOps.GetAllSymlinks(out symlinks, Context.dbInfo.id);
}
if(UpdateProgress != null)
UpdateProgress("", "Creating folders...", 4, 100);
#if DEBUG
stopwatch.Restart();
@@ -689,14 +736,35 @@ namespace osrepodbmgr.Core
Console.WriteLine("Core.SaveAs(): Took {0} seconds to create all folders", stopwatch.Elapsed.TotalSeconds);
#endif
if(UpdateProgress != null)
UpdateProgress("", "Creating symbolic links...", 4, 100);
#if DEBUG
stopwatch.Restart();
#endif
counter = 3;
counter = 0;
foreach(KeyValuePair<string, string> kvp in symlinks)
{
if(UpdateProgress2 != null)
UpdateProgress2("", kvp.Key, counter, folders.Count);
Symlinks.Symlink(kvp.Value, kvp.Key);
counter++;
}
#if DEBUG
stopwatch.Stop();
Console.WriteLine("Core.SaveAs(): Took {0} seconds to create all symbolic links", stopwatch.Elapsed.TotalSeconds);
#endif
#if DEBUG
stopwatch.Restart();
#endif
counter = 4;
foreach(DBOSFile file in files)
{
if(UpdateProgress != null)
UpdateProgress("", string.Format("Creating {0}...", file.Path), counter, 3 + files.Count);
UpdateProgress("", string.Format("Creating {0}...", file.Path), counter, 4 + files.Count);
Stream zStream = null;
string repoPath;

View File

@@ -76,6 +76,7 @@
<Compile Include="Workers\DiscImageChef.cs" />
<Compile Include="Workers\Clamd.cs" />
<Compile Include="Workers\VirusTotal.cs" />
<Compile Include="Symlinks.cs" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
@@ -125,10 +126,7 @@
<MonoDevelop>
<Properties>
<Policies>
<DotNetNamingPolicy DirectoryNamespaceAssociation="PrefixedHierarchical" ResourceNamePolicy="MSBuild">
<inheritsSet />
<inheritsScope />
</DotNetNamingPolicy>
<DotNetNamingPolicy DirectoryNamespaceAssociation="PrefixedHierarchical" ResourceNamePolicy="MSBuild" />
</Policies>
</Properties>
</MonoDevelop>