Implemented ArFileCreator

This commit is contained in:
Nikita Tsukanov
2017-10-06 01:02:00 +03:00
committed by Frederik Carlier
parent b224d540bf
commit b43ee62280
3 changed files with 148 additions and 21 deletions

View File

@@ -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);
}
}
}
}
}

View 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);
}
}
}

View File

@@ -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;
}
}
}