Skip to content

Commit

Permalink
Remove faulty ManagedWimLib
Browse files Browse the repository at this point in the history
  • Loading branch information
SuperJMN committed Nov 22, 2023
1 parent 2e2c6b2 commit bf408b8
Show file tree
Hide file tree
Showing 5 changed files with 158 additions and 107 deletions.
1 change: 0 additions & 1 deletion src/Deployer.Gui/Deployer.Desktop.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
<PackageReference Include="Autofac" Version="6.3.0" />
<PackageReference Include="Avalonia" Version="0.10.21" />
<PackageReference Include="Avalonia.Desktop" Version="0.10.21" />
<PackageReference Include="ManagedWimLib" Version="2.5.0" />
<PackageReference Include="XamlNameReferenceGenerator" Version="1.6.1" />
<PackageReference Include="Avalonia.ReactiveUI" Version="0.10.21" />
<PackageReference Include="Avalonia.Xaml.Behaviors" Version="0.10.19" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,16 +69,19 @@ public Maybe<DiskImageMetadata> SelectedImage
set => this.RaiseAndSetIfChanged(ref selectedImage , value);
}

private static IObservable<Result<IList<DiskImageMetadata>>> LoadImage(IWindowsImageMetadataReader windowsImageMetadataReader, IFileSystem fileSystem, string path)
private static IObservable<Result<IList<DiskImageMetadata>>> LoadImage(IWindowsImageMetadataReader windowsImageMetadataReader, IFileSystem fileSystem, string s)
{
if (!fileSystem.File.Exists(path))
if (!fileSystem.File.Exists(s))
{
return Observable.Return(Result.Failure<IList<DiskImageMetadata>>("The image doesn't exist"));
}

return Observable
.Return(windowsImageMetadataReader.Load(path)
.Map(x => x.Images));
return Observable.Using<Result<IList<DiskImageMetadata>>, Stream>(() => fileSystem.File.OpenRead(s),
stream =>
{
var result = windowsImageMetadataReader.Load(stream).Map(x => x.Images);
return Observable.Return(result);
});
}

public IEnumerable<DiskImageMetadata> Images => images.Value;
Expand Down
1 change: 0 additions & 1 deletion src/Deployer.Wim/Deployer.Wim.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@

<ItemGroup>
<PackageReference Include="CSharpFunctionalExtensions" Version="2.24.3" />
<PackageReference Include="ManagedWimLib" Version="2.5.0" />
<PackageReference Include="System.Text.Encodings.Web" Version="5.0.1" />
<PackageReference Include="Zafiro.Core" Version="3.0.4" />
</ItemGroup>
Expand Down
5 changes: 3 additions & 2 deletions src/Deployer.Wim/IWindowsImageMetadataReader.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
using CSharpFunctionalExtensions;
using System.IO;
using CSharpFunctionalExtensions;

namespace Deployer.Wim
{
public interface IWindowsImageMetadataReader
{
Result<XmlWindowsImageMetadata> Load(string path);
Result<XmlWindowsImageMetadata> Load(Stream stream);
}
}
245 changes: 147 additions & 98 deletions src/Deployer.Wim/WindowsImageMetadataReader.cs
Original file line number Diff line number Diff line change
@@ -1,138 +1,187 @@
using System;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Xml.Serialization;
using CSharpFunctionalExtensions;
using ManagedWimLib;
using Serilog;
using Zafiro.Core.Pending;
using Zafiro.System.Windows;

namespace Deployer.Wim;

public class WindowsImageMetadataReader : IWindowsImageMetadataReader
namespace Deployer.Wim
{
private static bool isInitialized;

public WindowsImageMetadataReader()
public class WindowsImageMetadataReader : IWindowsImageMetadataReader
{
if (isInitialized)
{
return;
}

InitNativeLibrary();
isInitialized = true;
}

private static XmlSerializer Serializer { get; } = new(typeof(WimMetadata));
private static XmlSerializer Serializer { get; } = new XmlSerializer(typeof(WimMetadata));

public Result<XmlWindowsImageMetadata> Load(string path)
{
return Result.Try(() => LoadCore(path));
}

private static void InitNativeLibrary()
{
var libBaseDir = AppDomain.CurrentDomain.BaseDirectory;
var libDir = "runtimes";
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
libDir = Path.Combine(libDir, "win-");
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
public Result<XmlWindowsImageMetadata> Load(Stream stream)
{
libDir = Path.Combine(libDir, "linux-");
Log.Verbose("Getting WIM stream");

WimMetadata metadata;
try
{
metadata = (WimMetadata)Serializer.Deserialize(GetXmlMetadataStream(stream));
}
catch (Exception e)
{
return Result.Failure<XmlWindowsImageMetadata>("Could not read the metadata from the WIM " +
$"file. Please, check it's a valid .WIM file: {e.Message}");
}

Log.Verbose("Wim metadata deserialized correctly {@Metadata}", metadata);

var images = from i in metadata.Images.Where(t => t.Windows is not null)
from a in GetArchitecture(i.Windows.Arch).ToEnumerable()
select new DiskImageMetadata
{
Architecture = a,
Build = i.Windows.Version.Build,
DisplayName = i.Name,
Index = int.Parse(i.Index)
};

return new XmlWindowsImageMetadata
{
Images = images.ToList(),
};
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))

private static Result<ProcessorArchitecture> GetArchitecture(string str)
{
libDir = Path.Combine(libDir, "osx-");
switch (str)
{
case "0":
return ProcessorArchitecture.X86;
case "9":
return ProcessorArchitecture.Amd64;
case "12":
return ProcessorArchitecture.Arm64;
}

return Result.Failure<ProcessorArchitecture>($"Cannot find architecture '{str}' is unknown");
}

switch (RuntimeInformation.ProcessArchitecture)
protected Stream GetXmlMetadataStream(Stream wim)
{
case Architecture.X86:
libDir += "x86";
break;
case Architecture.X64:
libDir += "x64";
break;
case Architecture.Arm:
libDir += "arm";
break;
case Architecture.Arm64:
libDir += "arm64";
break;
}
var outputstream = new MemoryStream();
var wimwriter = new BinaryWriter(outputstream);
using (var wimsecstream = wim)
{
using (var wimsecreader = new BinaryReader(wimsecstream))
{
var bytes = new byte[]
{
0x4D, 0x53, 0x57, 0x49, 0x4D
};

libDir = Path.Combine(libDir, "native");
Log.Verbose("(WIM) Finding Magic Bytes...");

// Some platforms require native library custom path to be an absolute path.
string libPath = null;
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
libPath = Path.Combine(libBaseDir, libDir, "libwim-15.dll");
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
libPath = Path.Combine(libBaseDir, libDir, "libwim.so");
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
libPath = Path.Combine(libBaseDir, libDir, "libwim.dylib");
var start = FindPosition(wimsecstream, bytes);

Log.Verbose("(WIM) Found Magic Bytes at " + start);

Log.Verbose("(WIM) Finding WIM XML Data...");

var endbytes = new byte[]
{
0x3C, 0x00, 0x2F, 0x00, 0x57, 0x00, 0x49, 0x00, 0x4D, 0x00, 0x3E, 0x00
};

wimsecstream.Seek(start + 72, SeekOrigin.Begin);
var buffer = new byte[24];
wimsecstream.Read(buffer, 0, 24);
var may = ToInt64LittleEndian(buffer, 8);
wimsecstream.Seek(start, SeekOrigin.Begin);

Log.Verbose("(WIM) Found WIM XML Data at " + start + may + 2);

wimsecstream.Seek(start + may + 2, SeekOrigin.Begin);

for (var i = wimsecstream.Position; i < wimsecstream.Length - endbytes.Length; i++)
{
if (BitConverter.ToString(wimsecreader.ReadBytes(12)) == BitConverter.ToString(endbytes))
{
wimwriter.Write(endbytes);
break;
}

wimsecstream.Seek(-12, SeekOrigin.Current);
wimwriter.Write(wimsecreader.ReadBytes(1));
}
}
}

outputstream.Seek(0, SeekOrigin.Begin);
return outputstream;
}

if (libPath == null)
private static long ToInt64LittleEndian(byte[] buffer, int offset)
{
throw new PlatformNotSupportedException("Unable to find native library.");
return (long)ToUInt64LittleEndian(buffer, offset);
}

if (!File.Exists(libPath))
private static uint ToUInt32LittleEndian(byte[] buffer, int offset)
{
throw new PlatformNotSupportedException($"Unable to find native library [{libPath}].");
var a = (buffer[offset + 3] << 24) & 0xFF000000U;
var b = (buffer[offset + 2] << 16) & 0x00FF0000U;
var c = (buffer[offset + 1] << 8) & 0x0000FF00U;
var d = (buffer[offset + 0] << 0) & 0x000000FFU;

return (uint)(a | b | c | d);
}

ManagedWimLib.Wim.GlobalInit(libPath, InitFlags.None);
}

private static Result<ProcessorArchitecture> GetArchitecture(string str)
{
switch (str)
private static ulong ToUInt64LittleEndian(byte[] buffer, int offset)
{
case "0":
return ProcessorArchitecture.X86;
case "9":
return ProcessorArchitecture.Amd64;
case "12":
return ProcessorArchitecture.Arm64;
return ((ulong)ToUInt32LittleEndian(buffer, offset + 4) << 32) | ToUInt32LittleEndian(buffer, offset + 0);
}

return Result.Failure<ProcessorArchitecture>($"Cannot find architecture '{str}' is unknown");
}

private XmlWindowsImageMetadata LoadCore(string path)
{
var xml = ManagedWimLib.Wim.OpenWim(path, OpenFlags.None).GetXmlData()[1..]; // Skip first (invalid) invisible char
var stringReader = new StringReader(xml);
//
// https://stackoverflow.com/questions/1471975/best-way-to-find-position-in-the-stream-where-given-byte-sequence-starts
//
public static long FindPosition(Stream stream, byte[] byteSequence)
{
if (byteSequence.Length > stream.Length)
{
return -1;
}

var metadata = (WimMetadata) Serializer.Deserialize(stringReader);
var buffer = new byte[byteSequence.Length];

Log.Verbose("Wim metadata deserialized correctly {@Metadata}", metadata);
var bufStream = new BufferedStream(stream, byteSequence.Length);

var images = from i in metadata.Images.Where(t => t.Windows is not null)
from a in GetArchitecture(i.Windows.Arch).ToEnumerable()
select new DiskImageMetadata
while ((bufStream.Read(buffer, 0, byteSequence.Length)) == byteSequence.Length)
{
Architecture = a,
Build = i.Windows.Version.Build,
DisplayName = i.Name,
Index = int.Parse(i.Index)
};
if (byteSequence.SequenceEqual(buffer))
{
return bufStream.Position - byteSequence.Length;
}

bufStream.Position -= byteSequence.Length - PadLeftSequence(buffer, byteSequence);
}

return -1;
}

return new XmlWindowsImageMetadata
private static int PadLeftSequence(byte[] bytes, byte[] seqBytes)
{
Images = images.ToList()
};
var i = 1;
while (i < bytes.Length)
{
var n = bytes.Length - i;
var aux1 = new byte[n];
var aux2 = new byte[n];
Array.Copy(bytes, i, aux1, 0, n);
Array.Copy(seqBytes, aux2, n);
if (aux1.SequenceEqual(aux2))
{
return i;
}

i++;
}

return i;
}
}
}

0 comments on commit bf408b8

Please sign in to comment.