Skip to content

Commit

Permalink
Merge pull request #560 from adamhathcock/gzip-fixes
Browse files Browse the repository at this point in the history
Expose Last Modified time on GZipStream.  Add CRC and Size to GZipEntries on Archive
  • Loading branch information
adamhathcock committed Jan 11, 2021
2 parents bdc57d3 + ee17dca commit 52c44be
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 16 deletions.
4 changes: 2 additions & 2 deletions src/SharpCompress/Common/GZip/GZipEntry.cs
Expand Up @@ -15,15 +15,15 @@ internal GZipEntry(GZipFilePart filePart)

public override CompressionType CompressionType => CompressionType.GZip;

public override long Crc => 0;
public override long Crc => _filePart.Crc ?? 0;

public override string Key => _filePart.FilePartName;

public override string? LinkTarget => null;

public override long CompressedSize => 0;

public override long Size => 0;
public override long Size => _filePart.UncompressedSize ?? 0;

public override DateTime? LastModifiedTime => _filePart.DateModified;

Expand Down
37 changes: 28 additions & 9 deletions src/SharpCompress/Common/GZip/GZipFilePart.cs
Expand Up @@ -16,14 +16,23 @@ internal sealed class GZipFilePart : FilePart
internal GZipFilePart(Stream stream, ArchiveEncoding archiveEncoding)
: base(archiveEncoding)
{
ReadAndValidateGzipHeader(stream);
EntryStartPosition = stream.Position;
_stream = stream;
ReadAndValidateGzipHeader();
if (stream.CanSeek)
{
long position = stream.Position;
stream.Position = stream.Length - 8;
ReadTrailer();
stream.Position = position;
}
EntryStartPosition = stream.Position;
}

internal long EntryStartPosition { get; }

internal DateTime? DateModified { get; private set; }
internal int? Crc { get; private set; }
internal int? UncompressedSize { get; private set; }

internal override string FilePartName => _name!;

Expand All @@ -37,11 +46,21 @@ internal override Stream GetRawStream()
return _stream;
}

private void ReadAndValidateGzipHeader(Stream stream)
private void ReadTrailer()
{
// Read and potentially verify the GZIP trailer: CRC32 and size mod 2^32
Span<byte> trailer = stackalloc byte[8];
int n = _stream.Read(trailer);

Crc = BinaryPrimitives.ReadInt32LittleEndian(trailer);
UncompressedSize = BinaryPrimitives.ReadInt32LittleEndian(trailer.Slice(4));
}

private void ReadAndValidateGzipHeader()
{
// read the header on the first read
Span<byte> header = stackalloc byte[10];
int n = stream.Read(header);
int n = _stream.Read(header);

// workitem 8501: handle edge case (decompress empty stream)
if (n == 0)
Expand All @@ -64,28 +83,28 @@ private void ReadAndValidateGzipHeader(Stream stream)
if ((header[3] & 0x04) == 0x04)
{
// read and discard extra field
n = stream.Read(header.Slice(0, 2)); // 2-byte length field
n = _stream.Read(header.Slice(0, 2)); // 2-byte length field

short extraLength = (short)(header[0] + header[1] * 256);
byte[] extra = new byte[extraLength];

if (!stream.ReadFully(extra))
if (!_stream.ReadFully(extra))
{
throw new ZlibException("Unexpected end-of-file reading GZIP header.");
}
n = extraLength;
}
if ((header[3] & 0x08) == 0x08)
{
_name = ReadZeroTerminatedString(stream);
_name = ReadZeroTerminatedString(_stream);
}
if ((header[3] & 0x10) == 0x010)
{
ReadZeroTerminatedString(stream);
ReadZeroTerminatedString(_stream);
}
if ((header[3] & 0x02) == 0x02)
{
stream.ReadByte(); // CRC16, ignore
_stream.ReadByte(); // CRC16, ignore
}
}

Expand Down
24 changes: 19 additions & 5 deletions src/SharpCompress/Compressors/Deflate/GZipStream.cs
Expand Up @@ -37,10 +37,9 @@ public class GZipStream : Stream
{
internal static readonly DateTime UNIX_EPOCH = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);

public DateTime? LastModified { get; set; }

private string? _comment;
private string? _fileName;
private DateTime? _lastModified;

internal ZlibBaseStream BaseStream;
private bool _disposed;
Expand Down Expand Up @@ -274,6 +273,7 @@ public override int Read(byte[] buffer, int offset, int count)
_firstReadDone = true;
FileName = BaseStream._GzipFileName;
Comment = BaseStream._GzipComment;
LastModified = BaseStream._GzipMtime;
}
return n;
}
Expand Down Expand Up @@ -358,6 +358,20 @@ public override void Write(byte[] buffer, int offset, int count)
}
}


public DateTime? LastModified
{
get => _lastModified;
set
{
if (_disposed)
{
throw new ObjectDisposedException(nameof(GZipStream));
}
_lastModified = value;
}
}

public string? FileName
{
get => _fileName;
Expand Down Expand Up @@ -398,8 +412,8 @@ private int EmitHeader()
byte[]? filenameBytes = (FileName is null) ? null
: _encoding.GetBytes(FileName);

int cbLength = (commentBytes is null) ? 0 : commentBytes.Length + 1;
int fnLength = (filenameBytes is null) ? 0 : filenameBytes.Length + 1;
int cbLength = commentBytes?.Length + 1 ?? 0;
int fnLength = filenameBytes?.Length + 1 ?? 0;

int bufferLength = 10 + cbLength + fnLength;
var header = new byte[bufferLength];
Expand All @@ -425,7 +439,7 @@ private int EmitHeader()
header[i++] = flag;

// mtime
if (!LastModified.HasValue)
if (LastModified is null)
{
LastModified = DateTime.Now;
}
Expand Down
14 changes: 14 additions & 0 deletions tests/SharpCompress.Test/GZip/GZipArchiveTests.cs
Expand Up @@ -22,6 +22,13 @@ public void GZip_Archive_Generic()
{
var entry = archive.Entries.First();
entry.WriteToFile(Path.Combine(SCRATCH_FILES_PATH, entry.Key));

long size = entry.Size;
var scratch = new FileInfo(Path.Combine(SCRATCH_FILES_PATH, "Tar.tar"));
var test = new FileInfo(Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar"));

Assert.Equal(size, scratch.Length);
Assert.Equal(size, test.Length);
}
CompareArchivesByPath(Path.Combine(SCRATCH_FILES_PATH, "Tar.tar"),
Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar"));
Expand All @@ -35,6 +42,13 @@ public void GZip_Archive()
{
var entry = archive.Entries.First();
entry.WriteToFile(Path.Combine(SCRATCH_FILES_PATH, entry.Key));

long size = entry.Size;
var scratch = new FileInfo(Path.Combine(SCRATCH_FILES_PATH, "Tar.tar"));
var test = new FileInfo(Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar"));

Assert.Equal(size, scratch.Length);
Assert.Equal(size, test.Length);
}
CompareArchivesByPath(Path.Combine(SCRATCH_FILES_PATH, "Tar.tar"),
Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar"));
Expand Down

0 comments on commit 52c44be

Please sign in to comment.