Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 89 additions & 0 deletions src/ICSharpCode.SharpZipLib/Tar/TarExtendedHeaderReader.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;

namespace ICSharpCode.SharpZipLib.Tar
{
public class TarExtendedHeaderReader
{
const byte LENGTH = 0;
const byte KEY = 1;
const byte VALUE = 2;
const byte END = 3;

private readonly Dictionary<string, string> headers = new Dictionary<string, string>();

private string[] headerParts = new string[3];

int bbIndex;
private byte[] byteBuffer;
private char[] charBuffer;

private readonly StringBuilder sb = new StringBuilder();
private readonly Decoder decoder = Encoding.UTF8.GetDecoder();

private int state = LENGTH;

private static readonly byte[] StateNext = new[] { (byte)' ', (byte)'=', (byte)'\n' };

public TarExtendedHeaderReader()
{
ResetBuffers();
}

public void Read(byte[] buffer, int length)
{
for (int i = 0; i < length; i++)
{
byte next = buffer[i];

if (next == StateNext[state])
{
Flush();
headerParts[state] = sb.ToString();
sb.Clear();

if (++state == END)
{
headers.Add(headerParts[KEY], headerParts[VALUE]);
headerParts = new string[3];
state = LENGTH;
}
}
else
{
byteBuffer[bbIndex++] = next;
if (bbIndex == 4)
Flush();
}
}
}

private void Flush()
{
decoder.Convert(byteBuffer, 0, bbIndex, charBuffer, 0, 4, false, out int bytesUsed, out int charsUsed, out bool completed);

sb.Append(charBuffer, 0, charsUsed);
ResetBuffers();
}

private void ResetBuffers()
{
charBuffer = new char[4];
byteBuffer = new byte[4];
bbIndex = 0;
}


public Dictionary<string, string> Headers
{
get
{
// TODO: Check for invalid state? -NM 2018-07-01
return headers;
}
}

}
}
25 changes: 24 additions & 1 deletion src/ICSharpCode.SharpZipLib/Tar/TarInputStream.cs
Original file line number Diff line number Diff line change
Expand Up @@ -434,7 +434,30 @@ public TarEntry GetNextEntry()
SkipToNextEntry();
headerBuf = this.tarBuffer.ReadBlock();
} else if (header.TypeFlag == TarHeader.LF_XHDR) { // POSIX extended header
// Ignore things we dont understand completely for now

byte[] nameBuffer = new byte[TarBuffer.BlockSize];
long numToRead = this.entrySize;

var xhr = new TarExtendedHeaderReader();

while (numToRead > 0)
{
int numRead = this.Read(nameBuffer, 0, (numToRead > nameBuffer.Length ? nameBuffer.Length : (int)numToRead));

if (numRead == -1)
{
throw new InvalidHeaderException("Failed to read long name entry");
}

xhr.Read(nameBuffer, numRead);
numToRead -= numRead;
}

if (xhr.Headers.TryGetValue("path", out string name))
{
longName = new StringBuilder(name);
}

SkipToNextEntry();
headerBuf = this.tarBuffer.ReadBlock();
} else if (header.TypeFlag == TarHeader.LF_GNU_VOLHDR) {
Expand Down
52 changes: 52 additions & 0 deletions test/ICSharpCode.SharpZipLib.Tests/Tar/TarTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,58 @@ public void LongNames()
}
}

[Test]
[Category("Tar")]
public void ExtendedHeaderLongName()
{
string expectedName = "lftest-0000000000111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999";

var input64 = @"Li9QYXhIZWFkZXJzLjExOTY5L2xmdGVzdC0wMDAwMDAwMDAwMTExMTExMTExMTIyMjIyMjIyMjIz
MzMzMzMzMzMzNDQ0NDQ0NDQ0NDU1NTU1NTU1NTU2NjY2NjY2NjY2Nzc3NzAwMDA2NDQAMDAwMDAw
MAAwMDAwMDAwADAwMDAwMDAwMzE3ADEzMzE2MTYyMzMzADAyMTYwNgAgeAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB1c3RhcgAwMAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAx
MTcgcGF0aD1sZnRlc3QtMDAwMDAwMDAwMDExMTExMTExMTEyMjIyMjIyMjIyMzMzMzMzMzMzMzQ0
NDQ0NDQ0NDQ1NTU1NTU1NTU1NjY2NjY2NjY2Njc3Nzc3Nzc3Nzc4ODg4ODg4ODg4OTk5OTk5OTk5
OQozMCBtdGltZT0xNTMwNDU1MjU5LjcwNjU0ODg4OAozMCBhdGltZT0xNTMwNDU1MjU5LjcwNjU0
ODg4OAozMCBjdGltZT0xNTMwNDU1MjU5LjcwNjU0ODg4OAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGxm
dGVzdC0wMDAwMDAwMDAwMTExMTExMTExMTIyMjIyMjIyMjIzMzMzMzMzMzMzNDQ0NDQ0NDQ0NDU1
NTU1NTU1NTU2NjY2NjY2NjY2Nzc3Nzc3Nzc3Nzg4ODg4ODg4ODg5OTkwMDAwNjY0ADAwMDE3NTAA
MDAwMTc1MAAwMDAwMDAwMDAwMAAxMzMxNjE2MjMzMwAwMjM3MjcAIDAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdXN0YXIAMDBuaWxzAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAG5pbHMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMDAwMDAwMAAwMDAwMDAw";

var buffer = new byte[2560];
var truncated = Convert.FromBase64String(input64);
Array.Copy(truncated, buffer, truncated.Length);
truncated = null;

using (var ms = new MemoryStream(buffer))
using (var tis = new TarInputStream(ms))
{
var entry = tis.GetNextEntry();
Assert.IsNotNull(entry, "Entry is null");

Assert.IsNotNull(entry.Name, "Entry name is null");

Assert.AreEqual(expectedName.Length, entry.Name.Length, $"Entry name is truncated to {entry.Name.Length} bytes.");

Assert.AreEqual(expectedName, entry.Name, "Entry name does not match expected value");
}

}


/// <summary>
/// Test equals function for tar headers.
/// </summary>
Expand Down