From dbde9e7104b907bbbaea17e21247d8cafc8b3a4c Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Sat, 14 Oct 2017 16:12:07 -0700 Subject: Massive refactoring to introduce the concept of IBackend --- .../Unbind/ExtractCabinetsCommand.cs | 146 +++++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 src/WixToolset.Core.WindowsInstaller/Unbind/ExtractCabinetsCommand.cs (limited to 'src/WixToolset.Core.WindowsInstaller/Unbind/ExtractCabinetsCommand.cs') diff --git a/src/WixToolset.Core.WindowsInstaller/Unbind/ExtractCabinetsCommand.cs b/src/WixToolset.Core.WindowsInstaller/Unbind/ExtractCabinetsCommand.cs new file mode 100644 index 00000000..229e75b4 --- /dev/null +++ b/src/WixToolset.Core.WindowsInstaller/Unbind/ExtractCabinetsCommand.cs @@ -0,0 +1,146 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +namespace WixToolset.Core.WindowsInstaller.Unbind +{ + using System; + using System.Collections; + using System.Collections.Specialized; + using System.Globalization; + using System.IO; + using WixToolset.Core.Cab; + using WixToolset.Data; + using WixToolset.Data.Rows; + using WixToolset.Msi; + + internal class ExtractCabinetsCommand + { + public ExtractCabinetsCommand(Output output, Database database, string inputFilePath, string exportBasePath, string intermediateFolder) + { + this.Output = output; + this.Database = database; + this.InputFilePath = inputFilePath; + this.ExportBasePath = exportBasePath; + this.IntermediateFolder = intermediateFolder; + } + + private Output Output { get; } + + private Database Database { get; } + + private string InputFilePath { get; } + + private string ExportBasePath { get; } + + private string IntermediateFolder { get; } + + public void Execute() + { + string databaseBasePath = Path.GetDirectoryName(this.InputFilePath); + StringCollection cabinetFiles = new StringCollection(); + SortedList embeddedCabinets = new SortedList(); + + // index all of the cabinet files + if (OutputType.Module == this.Output.Type) + { + embeddedCabinets.Add(0, "MergeModule.CABinet"); + } + else if (null != this.Output.Tables["Media"]) + { + foreach (MediaRow mediaRow in this.Output.Tables["Media"].Rows) + { + if (null != mediaRow.Cabinet) + { + if (OutputType.Product == this.Output.Type || + (OutputType.Transform == this.Output.Type && RowOperation.Add == mediaRow.Operation)) + { + if (mediaRow.Cabinet.StartsWith("#", StringComparison.Ordinal)) + { + embeddedCabinets.Add(mediaRow.DiskId, mediaRow.Cabinet.Substring(1)); + } + else + { + cabinetFiles.Add(Path.Combine(databaseBasePath, mediaRow.Cabinet)); + } + } + } + } + } + + // extract the embedded cabinet files from the database + if (0 < embeddedCabinets.Count) + { + using (View streamsView = this.Database.OpenView("SELECT `Data` FROM `_Streams` WHERE `Name` = ?")) + { + foreach (int diskId in embeddedCabinets.Keys) + { + using (Record record = new Record(1)) + { + record.SetString(1, (string)embeddedCabinets[diskId]); + streamsView.Execute(record); + } + + using (Record record = streamsView.Fetch()) + { + if (null != record) + { + // since the cabinets are stored in case-sensitive streams inside the msi, but the file system is not case-sensitive, + // embedded cabinets must be extracted to a canonical file name (like their diskid) to ensure extraction will always work + string cabinetFile = Path.Combine(this.IntermediateFolder, String.Concat("Media", Path.DirectorySeparatorChar, diskId.ToString(CultureInfo.InvariantCulture), ".cab")); + + // ensure the parent directory exists + System.IO.Directory.CreateDirectory(Path.GetDirectoryName(cabinetFile)); + + using (FileStream fs = System.IO.File.Create(cabinetFile)) + { + int bytesRead; + byte[] buffer = new byte[512]; + + while (0 != (bytesRead = record.GetStream(1, buffer, buffer.Length))) + { + fs.Write(buffer, 0, bytesRead); + } + } + + cabinetFiles.Add(cabinetFile); + } + else + { + // TODO: warning about missing embedded cabinet + } + } + } + } + } + + // extract the cabinet files + if (0 < cabinetFiles.Count) + { + string fileDirectory = Path.Combine(this.ExportBasePath, "File"); + + // delete the directory and its files to prevent cab extraction due to an existing file + if (Directory.Exists(fileDirectory)) + { + Directory.Delete(fileDirectory, true); + } + + // ensure the directory exists or extraction will fail + Directory.CreateDirectory(fileDirectory); + + foreach (string cabinetFile in cabinetFiles) + { + using (var extractCab = new WixExtractCab()) + { + try + { + extractCab.Extract(cabinetFile, fileDirectory); + } + catch (FileNotFoundException) + { + throw new WixException(WixErrors.FileNotFound(new SourceLineNumber(this.InputFilePath), cabinetFile)); + } + } + } + } + } + } +} -- cgit v1.2.3-55-g6feb