diff --git a/osrepodbmgr.Core/Context.cs b/osrepodbmgr.Core/Context.cs index 6dd4f15..cee76fe 100644 --- a/osrepodbmgr.Core/Context.cs +++ b/osrepodbmgr.Core/Context.cs @@ -54,6 +54,7 @@ namespace osrepodbmgr.Core public static bool usableDotNetZip; public static string clamdVersion; public static bool virusTotalEnabled; + public static Dictionary symlinks; public delegate void UnarChangeStatusDelegate(); public static event UnarChangeStatusDelegate UnarChangeStatus; diff --git a/osrepodbmgr.Core/DBOps.cs b/osrepodbmgr.Core/DBOps.cs index c37c54d..a4896ea 100644 --- a/osrepodbmgr.Core/DBOps.cs +++ b/osrepodbmgr.Core/DBOps.cs @@ -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 entries, long id) + { + entries = new Dictionary(); + + 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; + } } } diff --git a/osrepodbmgr.Core/Symlinks.cs b/osrepodbmgr.Core/Symlinks.cs new file mode 100644 index 0000000..2e50f34 --- /dev/null +++ b/osrepodbmgr.Core/Symlinks.cs @@ -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); + } + } +} \ No newline at end of file diff --git a/osrepodbmgr.Core/Workers/Compression.cs b/osrepodbmgr.Core/Workers/Compression.cs index b27ed0a..ce9043b 100644 --- a/osrepodbmgr.Core/Workers/Compression.cs +++ b/osrepodbmgr.Core/Workers/Compression.cs @@ -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) diff --git a/osrepodbmgr.Core/Workers/Database.cs b/osrepodbmgr.Core/Workers/Database.cs index a0de4c4..3d81c1d 100644 --- a/osrepodbmgr.Core/Workers/Database.cs +++ b/osrepodbmgr.Core/Workers/Database.cs @@ -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 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) diff --git a/osrepodbmgr.Core/Workers/Files.cs b/osrepodbmgr.Core/Workers/Files.cs index 06cf59b..389980a 100644 --- a/osrepodbmgr.Core/Workers/Files.cs +++ b/osrepodbmgr.Core/Workers/Files.cs @@ -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(); Context.foldersDict = new Dictionary(); + Context.symlinks = new Dictionary(); List alreadyMetadata = new List(); 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 architectures = new List(); @@ -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 files; List folders; + Dictionary symlinks = new Dictionary(); 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 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; diff --git a/osrepodbmgr.Core/osrepodbmgr.Core.csproj b/osrepodbmgr.Core/osrepodbmgr.Core.csproj index fd2a7b1..98b11a1 100644 --- a/osrepodbmgr.Core/osrepodbmgr.Core.csproj +++ b/osrepodbmgr.Core/osrepodbmgr.Core.csproj @@ -76,6 +76,7 @@ + @@ -125,10 +126,7 @@ - - - - +