Merge pull request #560 from adamhathcock/gzip-fixes

Expose Last Modified time on GZipStream.  Add CRC and Size to GZipEntries on Archive
This commit is contained in:
Adam Hathcock 2021-01-11 08:57:19 +00:00 committed by GitHub
commit 52c44befa2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 63 additions and 16 deletions

View file

@ -15,7 +15,7 @@ namespace SharpCompress.Common.GZip
public override CompressionType CompressionType => CompressionType.GZip;
public override long Crc => 0;
public override long Crc => _filePart.Crc ?? 0;
public override string Key => _filePart.FilePartName;
@ -23,7 +23,7 @@ namespace SharpCompress.Common.GZip
public override long CompressedSize => 0;
public override long Size => 0;
public override long Size => _filePart.UncompressedSize ?? 0;
public override DateTime? LastModifiedTime => _filePart.DateModified;

View file

@ -16,14 +16,23 @@ namespace SharpCompress.Common.GZip
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!;
@ -37,11 +46,21 @@ namespace SharpCompress.Common.GZip
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)
@ -64,12 +83,12 @@ namespace SharpCompress.Common.GZip
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.");
}
@ -77,15 +96,15 @@ namespace SharpCompress.Common.GZip
}
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
}
}

View file

@ -37,10 +37,9 @@ namespace SharpCompress.Compressors.Deflate
{
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;
@ -274,6 +273,7 @@ namespace SharpCompress.Compressors.Deflate
_firstReadDone = true;
FileName = BaseStream._GzipFileName;
Comment = BaseStream._GzipComment;
LastModified = BaseStream._GzipMtime;
}
return n;
}
@ -358,6 +358,20 @@ namespace SharpCompress.Compressors.Deflate
}
}
public DateTime? LastModified
{
get => _lastModified;
set
{
if (_disposed)
{
throw new ObjectDisposedException(nameof(GZipStream));
}
_lastModified = value;
}
}
public string? FileName
{
get => _fileName;
@ -398,8 +412,8 @@ namespace SharpCompress.Compressors.Deflate
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];
@ -425,7 +439,7 @@ namespace SharpCompress.Compressors.Deflate
header[i++] = flag;
// mtime
if (!LastModified.HasValue)
if (LastModified is null)
{
LastModified = DateTime.Now;
}

View file

@ -22,6 +22,13 @@ namespace SharpCompress.Test.GZip
{
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"));
@ -35,6 +42,13 @@ namespace SharpCompress.Test.GZip
{
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"));