From 1adcce6c62b3a5efcabaf890df9c01f957d4e7b1 Mon Sep 17 00:00:00 2001 From: Adam Hathcock Date: Sat, 9 Jan 2021 12:53:13 +0000 Subject: [PATCH 1/3] Expose Last Modified time on GZipStream --- .../Compressors/Deflate/GZipStream.cs | 24 +++++++++++++++---- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/src/SharpCompress/Compressors/Deflate/GZipStream.cs b/src/SharpCompress/Compressors/Deflate/GZipStream.cs index 903cea2c..84fc1379 100644 --- a/src/SharpCompress/Compressors/Deflate/GZipStream.cs +++ b/src/SharpCompress/Compressors/Deflate/GZipStream.cs @@ -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; } @@ -357,6 +357,20 @@ namespace SharpCompress.Compressors.Deflate _comment = value; } } + + + public DateTime? LastModified + { + get => _lastModified; + set + { + if (_disposed) + { + throw new ObjectDisposedException(nameof(GZipStream)); + } + _lastModified = value; + } + } public string? 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; } From 8598885258df433e11c2955770c39d5510a909d0 Mon Sep 17 00:00:00 2001 From: Adam Hathcock Date: Sat, 9 Jan 2021 13:22:06 +0000 Subject: [PATCH 2/3] Read trailer for GZip for CRC and uncompressed size --- src/SharpCompress/Common/GZip/GZipEntry.cs | 6 +-- src/SharpCompress/Common/GZip/GZipFilePart.cs | 37 ++++++++++++++----- .../GZip/GZipArchiveTests.cs | 14 +++++++ 3 files changed, 45 insertions(+), 12 deletions(-) diff --git a/src/SharpCompress/Common/GZip/GZipEntry.cs b/src/SharpCompress/Common/GZip/GZipEntry.cs index f5985534..07a3d491 100644 --- a/src/SharpCompress/Common/GZip/GZipEntry.cs +++ b/src/SharpCompress/Common/GZip/GZipEntry.cs @@ -15,15 +15,15 @@ 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; - public override string? LinkTarget => null; + 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; diff --git a/src/SharpCompress/Common/GZip/GZipFilePart.cs b/src/SharpCompress/Common/GZip/GZipFilePart.cs index 2ac205e1..03b5b9ae 100644 --- a/src/SharpCompress/Common/GZip/GZipFilePart.cs +++ b/src/SharpCompress/Common/GZip/GZipFilePart.cs @@ -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 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 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 } } diff --git a/tests/SharpCompress.Test/GZip/GZipArchiveTests.cs b/tests/SharpCompress.Test/GZip/GZipArchiveTests.cs index 8476724a..0fbb1602 100644 --- a/tests/SharpCompress.Test/GZip/GZipArchiveTests.cs +++ b/tests/SharpCompress.Test/GZip/GZipArchiveTests.cs @@ -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")); From ee17dca9e5bedef545aeb1fff06bfc228e07965f Mon Sep 17 00:00:00 2001 From: Adam Hathcock Date: Sat, 9 Jan 2021 13:36:30 +0000 Subject: [PATCH 3/3] Fix formatting --- src/SharpCompress/Common/GZip/GZipEntry.cs | 2 +- src/SharpCompress/Compressors/Deflate/GZipStream.cs | 4 ++-- tests/SharpCompress.Test/GZip/GZipArchiveTests.cs | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/SharpCompress/Common/GZip/GZipEntry.cs b/src/SharpCompress/Common/GZip/GZipEntry.cs index 07a3d491..1204cfd7 100644 --- a/src/SharpCompress/Common/GZip/GZipEntry.cs +++ b/src/SharpCompress/Common/GZip/GZipEntry.cs @@ -19,7 +19,7 @@ namespace SharpCompress.Common.GZip public override string Key => _filePart.FilePartName; - public override string? LinkTarget => null; + public override string? LinkTarget => null; public override long CompressedSize => 0; diff --git a/src/SharpCompress/Compressors/Deflate/GZipStream.cs b/src/SharpCompress/Compressors/Deflate/GZipStream.cs index 84fc1379..ffc4759d 100644 --- a/src/SharpCompress/Compressors/Deflate/GZipStream.cs +++ b/src/SharpCompress/Compressors/Deflate/GZipStream.cs @@ -357,8 +357,8 @@ namespace SharpCompress.Compressors.Deflate _comment = value; } } - - + + public DateTime? LastModified { get => _lastModified; diff --git a/tests/SharpCompress.Test/GZip/GZipArchiveTests.cs b/tests/SharpCompress.Test/GZip/GZipArchiveTests.cs index 0fbb1602..5f4a7c22 100644 --- a/tests/SharpCompress.Test/GZip/GZipArchiveTests.cs +++ b/tests/SharpCompress.Test/GZip/GZipArchiveTests.cs @@ -26,7 +26,7 @@ namespace SharpCompress.Test.GZip 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); } @@ -46,7 +46,7 @@ namespace SharpCompress.Test.GZip 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); }