mirror of
https://github.com/quamotion/dotnet-packaging.git
synced 2026-02-04 05:35:40 +00:00
Implemented ArFileCreator
This commit is contained in:
committed by
Frederik Carlier
parent
b224d540bf
commit
b43ee62280
@@ -1,6 +1,8 @@
|
||||
using Packaging.Targets.IO;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Xunit;
|
||||
|
||||
namespace Packaging.Targets.Tests.IO
|
||||
@@ -40,5 +42,60 @@ namespace Packaging.Targets.Tests.IO
|
||||
filenames);
|
||||
}
|
||||
}
|
||||
|
||||
ArHeader CloneHeader(ArHeader hdr)
|
||||
{
|
||||
return new ArHeader
|
||||
{
|
||||
EndChar = hdr.EndChar,
|
||||
FileMode = hdr.FileMode,
|
||||
FileName = hdr.FileName,
|
||||
FileSize = hdr.FileSize,
|
||||
GroupId = hdr.GroupId,
|
||||
OwnerId = hdr.OwnerId,
|
||||
LastModified = hdr.LastModified
|
||||
};
|
||||
}
|
||||
|
||||
void AssertCompareClonedHeader(ArHeader original, ArHeader clone)
|
||||
{
|
||||
var ms = new MemoryStream();
|
||||
ms.WriteStruct(clone);
|
||||
ms.Seek(0, SeekOrigin.Begin);
|
||||
clone = ms.ReadStruct<ArHeader>();
|
||||
foreach (var f in typeof(ArHeader).GetFields(BindingFlags.Instance | BindingFlags.NonPublic |
|
||||
BindingFlags.Public))
|
||||
{
|
||||
var orig = f.GetValue(original);
|
||||
var mod = f.GetValue(clone);
|
||||
if (mod is byte[] modchars)
|
||||
Assert.True(modchars.SequenceEqual((byte[]) orig), $"Failed check for {f.Name}");
|
||||
else
|
||||
Assert.True(orig.Equals(mod), $"Failed check for {f.Name}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
[Fact]
|
||||
public void WriteTest()
|
||||
{
|
||||
using (Stream original = File.OpenRead("Deb/libplist3_1.12-3.1_amd64.deb"))
|
||||
using (Stream expected = File.OpenRead("Deb/libplist3_1.12-3.1_amd64.deb"))
|
||||
using (Stream actual = new MemoryStream())
|
||||
using (Stream output = new ValidatingCompositeStream(null, actual, expected))
|
||||
{
|
||||
ArFileCreator.WriteMagic(output);
|
||||
var input = new ArFile(original, true);
|
||||
while (input.Read())
|
||||
{
|
||||
var header = (ArHeader) input.FileHeader;
|
||||
var clone = CloneHeader(header);
|
||||
AssertCompareClonedHeader(header, clone);
|
||||
using(var data = input.Open())
|
||||
ArFileCreator.WriteEntry(output, clone, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
38
Packaging.Targets/IO/ArFileCreator.cs
Normal file
38
Packaging.Targets/IO/ArFileCreator.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace Packaging.Targets.IO
|
||||
{
|
||||
public static class ArFileCreator
|
||||
{
|
||||
public static void WriteMagic(Stream output)
|
||||
{
|
||||
var wr = new StreamWriter(output);
|
||||
wr.Write("!<arch>\n");
|
||||
wr.Flush();
|
||||
}
|
||||
|
||||
public static void WriteEntry(Stream output, string name, LinuxFileMode mode, Stream data)
|
||||
{
|
||||
var hdr = new ArHeader
|
||||
{
|
||||
EndChar = "`\n",
|
||||
FileMode = mode,
|
||||
FileName = name,
|
||||
FileSize = (uint)data.Length,
|
||||
GroupId = 0,
|
||||
OwnerId = 0,
|
||||
LastModified = DateTimeOffset.UtcNow
|
||||
};
|
||||
WriteEntry(output, hdr, data);
|
||||
}
|
||||
|
||||
public static void WriteEntry(Stream output, ArHeader header, Stream data)
|
||||
{
|
||||
output.WriteStruct(header);
|
||||
data.CopyTo(output);
|
||||
if (output.Position % 2 != 0)
|
||||
output.WriteByte(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace Packaging.Targets.IO
|
||||
{
|
||||
@@ -9,33 +10,33 @@ namespace Packaging.Targets.IO
|
||||
public struct ArHeader : IArchiveHeader
|
||||
{
|
||||
[MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.U1, SizeConst = 16)]
|
||||
private char[] fileName;
|
||||
private byte[] fileName;
|
||||
|
||||
[MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.U1, SizeConst = 12)]
|
||||
private char[] lastModified;
|
||||
private byte[] lastModified;
|
||||
|
||||
[MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.U1, SizeConst = 6)]
|
||||
private char[] ownerId;
|
||||
private byte[] ownerId;
|
||||
|
||||
[MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.U1, SizeConst = 6)]
|
||||
private char[] groupId;
|
||||
private byte[] groupId;
|
||||
|
||||
[MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.U1, SizeConst = 8)]
|
||||
private char[] fileMode;
|
||||
private byte[] fileMode;
|
||||
|
||||
[MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.U1, SizeConst = 10)]
|
||||
private char[] fileSize;
|
||||
private byte[] fileSize;
|
||||
|
||||
[MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.U1, SizeConst = 2)]
|
||||
private char[] endChar;
|
||||
private byte[] endChar;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name of the current file.
|
||||
/// </summary>
|
||||
public string FileName
|
||||
{
|
||||
get { return new string(this.fileName).Trim(); }
|
||||
set { this.fileName = value.ToCharArray(); }
|
||||
get => GetString(fileName, 16).Trim();
|
||||
set => fileName = CreateString(value, 16);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -43,8 +44,8 @@ namespace Packaging.Targets.IO
|
||||
/// </summary>
|
||||
public DateTimeOffset LastModified
|
||||
{
|
||||
get { return DateTimeOffset.FromUnixTimeSeconds((long)Convert.ToUInt32(new string(this.lastModified).Trim())); }
|
||||
set { this.lastModified = value.ToUnixTimeSeconds().ToString("x8").PadRight(12).ToCharArray(); }
|
||||
get => DateTimeOffset.FromUnixTimeSeconds(int.Parse(GetString(lastModified, 12).Trim()));
|
||||
set => this.lastModified = CreateString(value.ToUnixTimeSeconds().ToString(), 12);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -52,8 +53,8 @@ namespace Packaging.Targets.IO
|
||||
/// </summary>
|
||||
public uint OwnerId
|
||||
{
|
||||
get { return Convert.ToUInt32(new string(this.ownerId).Trim()); }
|
||||
set { this.ownerId = value.ToString().PadRight(6).ToCharArray(); }
|
||||
get => ReadUInt(ownerId);
|
||||
set => ownerId = CreateString(value.ToString(), 6);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -61,22 +62,22 @@ namespace Packaging.Targets.IO
|
||||
/// </summary>
|
||||
public uint GroupId
|
||||
{
|
||||
get { return Convert.ToUInt32(new string(this.groupId).Trim()); }
|
||||
set { this.groupId = value.ToString().PadRight(6).ToCharArray(); }
|
||||
get => ReadUInt(groupId);
|
||||
set => groupId = CreateString(value.ToString(), 6);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public LinuxFileMode FileMode
|
||||
{
|
||||
get { return (LinuxFileMode)Convert.ToUInt32(new string(this.fileMode).Trim(), 8); }
|
||||
set { this.fileMode = ((uint)value).ToString("x8").PadRight(8).ToCharArray(); }
|
||||
get => (LinuxFileMode) Convert.ToUInt32(GetString(fileMode, 8).Trim(), 8);
|
||||
set => fileMode = CreateString(Convert.ToString((uint)value, 8), 8);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public uint FileSize
|
||||
{
|
||||
get { return Convert.ToUInt32(new string(this.fileSize).Trim()); }
|
||||
set { this.fileSize = value.ToString().PadRight(10).ToCharArray(); }
|
||||
get => ReadUInt(fileSize);
|
||||
set => fileSize = CreateString(value.ToString(), 10);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -84,8 +85,8 @@ namespace Packaging.Targets.IO
|
||||
/// </summary>
|
||||
public string EndChar
|
||||
{
|
||||
get { return new string(this.endChar); }
|
||||
set { this.endChar = value.ToCharArray(); }
|
||||
get => GetString(endChar, 2);
|
||||
set => endChar = CreateString(value, 2);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
@@ -93,5 +94,36 @@ namespace Packaging.Targets.IO
|
||||
{
|
||||
return this.FileName;
|
||||
}
|
||||
|
||||
private string GetString(byte[] data, int maxLen)
|
||||
{
|
||||
int len;
|
||||
for (len = 0; len < maxLen; len++)
|
||||
{
|
||||
if (data[len] == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (len == 0)
|
||||
return null;
|
||||
return Encoding.UTF8.GetString(data, 0, len);
|
||||
}
|
||||
|
||||
|
||||
uint ReadUInt(byte[] data) => Convert.ToUInt32(GetString(data, data.Length).Trim());
|
||||
|
||||
byte[] CreateString(string s, int len)
|
||||
{
|
||||
var target = new byte[len];
|
||||
if (s == null)
|
||||
return target;
|
||||
var buffer = Encoding.UTF8.GetBytes(s);
|
||||
if (buffer.Length > len)
|
||||
throw new Exception($"String {s} exceeds the limit of {len}");
|
||||
for (var c = 0; c < len; c++)
|
||||
target[c] = (c < buffer.Length) ? buffer[c] : (byte) 0x20;
|
||||
return target;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user